From nobody Sun May 5 00:58:20 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of redhat.com designates 209.132.183.28 as permitted sender) client-ip=209.132.183.28; envelope-from=libvir-list-bounces@redhat.com; helo=mx1.redhat.com; Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zoho.com: domain of redhat.com designates 209.132.183.28 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com Return-Path: Received: from mx1.redhat.com (mx1.redhat.com [209.132.183.28]) by mx.zohomail.com with SMTPS id 15088665401013.5234032999192095; Tue, 24 Oct 2017 10:35:40 -0700 (PDT) Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.14]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 14723997E5; Tue, 24 Oct 2017 17:35:38 +0000 (UTC) Received: from colo-mx.corp.redhat.com (colo-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.21]) by smtp.corp.redhat.com (Postfix) with ESMTPS id E12BF5DA6E; Tue, 24 Oct 2017 17:35:37 +0000 (UTC) Received: from lists01.pubmisc.prod.ext.phx2.redhat.com (lists01.pubmisc.prod.ext.phx2.redhat.com [10.5.19.33]) by colo-mx.corp.redhat.com (Postfix) with ESMTP id A7E926EF24; Tue, 24 Oct 2017 17:35:37 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.phx2.redhat.com [10.5.11.13]) by lists01.pubmisc.prod.ext.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id v9OHZLIx024656 for ; Tue, 24 Oct 2017 13:35:21 -0400 Received: by smtp.corp.redhat.com (Postfix) id A4E5F6BF8A; Tue, 24 Oct 2017 17:35:21 +0000 (UTC) Received: from mx1.redhat.com (ext-mx10.extmail.prod.ext.phx2.redhat.com [10.5.110.39]) by smtp.corp.redhat.com (Postfix) with ESMTPS id B0CDD6BF90; Tue, 24 Oct 2017 17:35:20 +0000 (UTC) Received: from mail-pf0-f196.google.com (mail-pf0-f196.google.com [209.85.192.196]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id B054962E82; Tue, 24 Oct 2017 17:35:15 +0000 (UTC) Received: by mail-pf0-f196.google.com with SMTP id p87so20178642pfj.3; Tue, 24 Oct 2017 10:35:15 -0700 (PDT) Received: from ps-f25-dev.eng.nutanix.com ([205.209.132.2]) by smtp.gmail.com with ESMTPSA id k2sm1333236pff.126.2017.10.24.10.35.12 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 24 Oct 2017 10:35:12 -0700 (PDT) DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com 14723997E5 Authentication-Results: ext-mx09.extmail.prod.ext.phx2.redhat.com; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: ext-mx09.extmail.prod.ext.phx2.redhat.com; spf=fail smtp.mailfrom=libvir-list-bounces@redhat.com DKIM-Filter: OpenDKIM Filter v2.11.0 mx1.redhat.com 14723997E5 Authentication-Results: mx1.redhat.com; dkim=fail reason="signature verification failed" (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="ZI1pjzjK" DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com B054962E82 Authentication-Results: ext-mx10.extmail.prod.ext.phx2.redhat.com; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: ext-mx10.extmail.prod.ext.phx2.redhat.com; spf=pass smtp.mailfrom=saxenap.ltc@gmail.com DKIM-Filter: OpenDKIM Filter v2.11.0 mx1.redhat.com B054962E82 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=1X3ZWwd1saBSkec5hAMebWTQhb/kvlk0040SgYJQVLo=; b=ZI1pjzjKXeNHAuqc0ULUMBH2lkDtR0WXHA3mt6lzLm0Hl+H/U11utx0gthIrrf4RX/ nuymP8J+hq0+IF6sKq1/JtFCXL2kZ8aqfygOLQdlHHmGUIImj+0gaHTrRuy2eDkphvmP UliGeAN/7RRAzgMpi+aova7CFjMl5jNhX2wAYoGr6x7k9573TCWA8FC4kBEwimudBi01 w5GbHYcPuBgaCb3m0cYeT2ZISWSSlxWdI/QL3U1GbXCOC5S3wCksHHgKRqZRzqWAz51F WixNbGhMKKZYFgF3gDXjeioKkh3VRfFMtl+S13da498X1eUqAQ+vmk7WW50QLvnW1s3j VatQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=1X3ZWwd1saBSkec5hAMebWTQhb/kvlk0040SgYJQVLo=; b=Vmb3hofLv6OhrlYRLd4N+uxgNys05pv6Y8XKPResgPJttIppU2i5Q6pXhBZIk2QMmz 6iuju28mCV6++ymbaMLul5rTTZpZKxAba1ipkTtQd5bTVCTKFiQuflD/jcnYlEIBOy2E 4Vmz9YDo9hzbbsmHBucJ0BeoekFbxj+oeRqqvTjMWO60AnZDeoAD5X2L59ab6jdKKf+K QHPS1TXlF5OD0V16z9bmoNNJG42BEXS20+nLX5uL2p8nOW/GE/DSv7GWt4TKnpCRtxQn ZgAqfcuDlK67Y45bhHNCSa8xf627RzzP4H1wXwdlAmEEGQRRuZVFz/U/KEbw0GYJepxD N0Pw== X-Gm-Message-State: AMCzsaXiDS2JFhJGJEQFiixGAffrKgMhLF8Q1cVdaDfMLgC4e2eFoyA9 D5fS/f57LRoOb5DaM3h5fdGyAw== X-Google-Smtp-Source: ABhQp+SaHW5aYePgbKBhjUV5feMqeSQgibz2YRYNRY41bJ+3Wn/W/Iva/PoJqms8Y5NWqNjd7IVWRw== X-Received: by 10.99.132.72 with SMTP id k69mr13121323pgd.141.1508866513696; Tue, 24 Oct 2017 10:35:13 -0700 (PDT) From: Prerna Saxena To: libvir-list@redhat.com Date: Tue, 24 Oct 2017 10:34:59 -0700 Message-Id: <20171024173501.18105-7-saxenap.ltc@gmail.com> In-Reply-To: <20171024173501.18105-1-saxenap.ltc@gmail.com> References: <20171024173501.18105-1-saxenap.ltc@gmail.com> X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.39]); Tue, 24 Oct 2017 17:35:16 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.39]); Tue, 24 Oct 2017 17:35:16 +0000 (UTC) for IP:'209.85.192.196' DOMAIN:'mail-pf0-f196.google.com' HELO:'mail-pf0-f196.google.com' FROM:'saxenap.ltc@gmail.com' RCPT:'' X-RedHat-Spam-Score: -2.41 (DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, FREEMAIL_FROM, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2, RCVD_IN_SORBS_SPAM, SPF_PASS) 209.85.192.196 mail-pf0-f196.google.com 209.85.192.196 mail-pf0-f196.google.com X-Scanned-By: MIMEDefang 2.78 on 10.5.110.39 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.13 X-loop: libvir-list@redhat.com Cc: --jdenemar@redhat.com, --pkrempa@redhat.com Subject: [libvirt] [[RFC] 6/8] Code refactor: Move helper functions of doCoreDump*, syncNicRxFilter*, and qemuOpenFile* to qemu_process.[ch] X-BeenThere: libvir-list@redhat.com X-Mailman-Version: 2.1.12 Precedence: junk List-Id: Development discussions about the libvirt library & tools List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Sender: libvir-list-bounces@redhat.com Errors-To: libvir-list-bounces@redhat.com X-Scanned-By: MIMEDefang 2.79 on 10.5.11.14 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.38]); Tue, 24 Oct 2017 17:35:38 +0000 (UTC) X-ZohoMail-DKIM: fail (Header signature does not verify) X-ZohoMail: RDKM_2 RSF_0 Z_629925259 SPT_0 Content-Type: text/plain; charset="utf-8" Signed-off-by: Prerna Saxena --- src/qemu/qemu_driver.c | 1161 -------------------------------------------= ---- src/qemu/qemu_process.c | 1133 +++++++++++++++++++++++++++++++++++++++++++= ++ src/qemu/qemu_process.h | 86 ++++ 3 files changed, 1219 insertions(+), 1161 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index b249347..9d495fb 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -151,11 +151,6 @@ static int qemuDomainObjStart(virConnectPtr conn, static int qemuDomainManagedSaveLoad(virDomainObjPtr vm, void *opaque); =20 -static int qemuOpenFileAs(uid_t fallback_uid, gid_t fallback_gid, - bool dynamicOwnership, - const char *path, int oflags, - bool *needUnlink, bool *bypassSecurityDriver); - static int qemuGetDHCPInterfaces(virDomainPtr dom, virDomainObjPtr vm, virDomainInterfacePtr **ifaces); @@ -2819,38 +2814,6 @@ qemuDomainGetControlInfo(virDomainPtr dom, =20 verify(sizeof(QEMU_SAVE_MAGIC) =3D=3D sizeof(QEMU_SAVE_PARTIAL)); =20 -typedef enum { - QEMU_SAVE_FORMAT_RAW =3D 0, - QEMU_SAVE_FORMAT_GZIP =3D 1, - QEMU_SAVE_FORMAT_BZIP2 =3D 2, - /* - * Deprecated by xz and never used as part of a release - * QEMU_SAVE_FORMAT_LZMA - */ - QEMU_SAVE_FORMAT_XZ =3D 3, - QEMU_SAVE_FORMAT_LZOP =3D 4, - /* Note: add new members only at the end. - These values are used in the on-disk format. - Do not change or re-use numbers. */ - - QEMU_SAVE_FORMAT_LAST -} virQEMUSaveFormat; - -VIR_ENUM_DECL(qemuSaveCompression) -VIR_ENUM_IMPL(qemuSaveCompression, QEMU_SAVE_FORMAT_LAST, - "raw", - "gzip", - "bzip2", - "xz", - "lzop") - -VIR_ENUM_DECL(qemuDumpFormat) -VIR_ENUM_IMPL(qemuDumpFormat, VIR_DOMAIN_CORE_DUMP_FORMAT_LAST, - "elf", - "kdump-zlib", - "kdump-lzo", - "kdump-snappy") - typedef struct _virQEMUSaveHeader virQEMUSaveHeader; typedef virQEMUSaveHeader *virQEMUSaveHeaderPtr; struct _virQEMUSaveHeader { @@ -3062,214 +3025,6 @@ qemuCompressGetCommand(virQEMUSaveFormat compressio= n) return ret; } =20 -/** - * qemuOpenFile: - * @driver: driver object - * @vm: domain object - * @path: path to file to open - * @oflags: flags for opening/creation of the file - * @needUnlink: set to true if file was created by this function - * @bypassSecurityDriver: optional pointer to a boolean that will be set t= o true - * if security driver operations are pointless (due= to - * NFS mount) - * - * Internal function to properly create or open existing files, with - * ownership affected by qemu driver setup and domain DAC label. - * - * Returns the file descriptor on success and negative errno on failure. - * - * This function should not be used on storage sources. Use - * qemuDomainStorageFileInit and storage driver APIs if possible. - **/ -static int -qemuOpenFile(virQEMUDriverPtr driver, - virDomainObjPtr vm, - const char *path, - int oflags, - bool *needUnlink, - bool *bypassSecurityDriver) -{ - int ret =3D -1; - virQEMUDriverConfigPtr cfg =3D virQEMUDriverGetConfig(driver); - uid_t user =3D cfg->user; - gid_t group =3D cfg->group; - bool dynamicOwnership =3D cfg->dynamicOwnership; - virSecurityLabelDefPtr seclabel; - - virObjectUnref(cfg); - - /* TODO: Take imagelabel into account? */ - if (vm && - (seclabel =3D virDomainDefGetSecurityLabelDef(vm->def, "dac")) != =3D NULL && - seclabel->label !=3D NULL && - (virParseOwnershipIds(seclabel->label, &user, &group) < 0)) - goto cleanup; - - ret =3D qemuOpenFileAs(user, group, dynamicOwnership, - path, oflags, needUnlink, bypassSecurityDriver); - - cleanup: - return ret; -} - -static int -qemuOpenFileAs(uid_t fallback_uid, gid_t fallback_gid, - bool dynamicOwnership, - const char *path, int oflags, - bool *needUnlink, bool *bypassSecurityDriver) -{ - struct stat sb; - bool is_reg =3D true; - bool need_unlink =3D false; - bool bypass_security =3D false; - unsigned int vfoflags =3D 0; - int fd =3D -1; - int path_shared =3D virFileIsSharedFS(path); - uid_t uid =3D geteuid(); - gid_t gid =3D getegid(); - - /* path might be a pre-existing block dev, in which case - * we need to skip the create step, and also avoid unlink - * in the failure case */ - if (oflags & O_CREAT) { - need_unlink =3D true; - - /* Don't force chown on network-shared FS - * as it is likely to fail. */ - if (path_shared <=3D 0 || dynamicOwnership) - vfoflags |=3D VIR_FILE_OPEN_FORCE_OWNER; - - if (stat(path, &sb) =3D=3D 0) { - /* It already exists, we don't want to delete it on error */ - need_unlink =3D false; - - is_reg =3D !!S_ISREG(sb.st_mode); - /* If the path is regular file which exists - * already and dynamic_ownership is off, we don't - * want to change its ownership, just open it as-is */ - if (is_reg && !dynamicOwnership) { - uid =3D sb.st_uid; - gid =3D sb.st_gid; - } - } - } - - /* First try creating the file as root */ - if (!is_reg) { - if ((fd =3D open(path, oflags & ~O_CREAT)) < 0) { - fd =3D -errno; - goto error; - } - } else { - if ((fd =3D virFileOpenAs(path, oflags, S_IRUSR | S_IWUSR, uid, gi= d, - vfoflags | VIR_FILE_OPEN_NOFORK)) < 0) { - /* If we failed as root, and the error was permission-denied - (EACCES or EPERM), assume it's on a network-connected share - where root access is restricted (eg, root-squashed NFS). If= the - qemu user is non-root, just set a flag to - bypass security driver shenanigans, and retry the operation - after doing setuid to qemu user */ - if ((fd !=3D -EACCES && fd !=3D -EPERM) || fallback_uid =3D=3D= geteuid()) - goto error; - - /* On Linux we can also verify the FS-type of the directory. */ - switch (path_shared) { - case 1: - /* it was on a network share, so we'll continue - * as outlined above - */ - break; - - case -1: - virReportSystemError(-fd, oflags & O_CREAT - ? _("Failed to create file " - "'%s': couldn't determine fs = type") - : _("Failed to open file " - "'%s': couldn't determine fs = type"), - path); - goto cleanup; - - case 0: - default: - /* local file - log the error returned by virFileOpenA= s */ - goto error; - } - - /* If we created the file above, then we need to remove it; - * otherwise, the next attempt to create will fail. If the - * file had already existed before we got here, then we also - * don't want to delete it and allow the following to succeed - * or fail based on existing protections - */ - if (need_unlink) - unlink(path); - - /* Retry creating the file as qemu user */ - - /* Since we're passing different modes... */ - vfoflags |=3D VIR_FILE_OPEN_FORCE_MODE; - - if ((fd =3D virFileOpenAs(path, oflags, - S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP, - fallback_uid, fallback_gid, - vfoflags | VIR_FILE_OPEN_FORK)) < 0) { - virReportSystemError(-fd, oflags & O_CREAT - ? _("Error from child process creatin= g '%s'") - : _("Error from child process opening= '%s'"), - path); - goto cleanup; - } - - /* Since we had to setuid to create the file, and the fstype - is NFS, we assume it's a root-squashing NFS share, and that - the security driver stuff would have failed anyway */ - - bypass_security =3D true; - } - } - cleanup: - if (needUnlink) - *needUnlink =3D need_unlink; - if (bypassSecurityDriver) - *bypassSecurityDriver =3D bypass_security; - return fd; - - error: - virReportSystemError(-fd, oflags & O_CREAT - ? _("Failed to create file '%s'") - : _("Failed to open file '%s'"), - path); - goto cleanup; -} - - -static int -qemuFileWrapperFDClose(virDomainObjPtr vm, - virFileWrapperFdPtr fd) -{ - int ret; - - /* virFileWrapperFd uses iohelper to write data onto disk. - * However, iohelper calls fdatasync() which may take ages to - * finish. Therefore, we shouldn't be waiting with the domain - * object locked. */ - - /* XXX Currently, this function is intended for *Save() only - * as restore needs some reworking before it's ready for - * this. */ - - virObjectUnlock(vm); - ret =3D virFileWrapperFdClose(fd); - virObjectLock(vm); - if (!virDomainObjIsActive(vm)) { - if (!virGetLastError()) - virReportError(VIR_ERR_OPERATION_FAILED, "%s", - _("domain is no longer running")); - ret =3D -1; - } - return ret; -} - =20 /* Helper function to execute a migration to file with a correct save head= er * the caller needs to make sure that the processors are stopped and do al= l other @@ -3481,82 +3236,6 @@ qemuDomainSaveInternal(virQEMUDriverPtr driver, virD= omainPtr dom, return ret; } =20 - -/* qemuGetCompressionProgram: - * @imageFormat: String representation from qemu.conf for the compression - * image format being used (dump, save, or snapshot). - * @compresspath: Pointer to a character string to store the fully qualifi= ed - * path from virFindFileInPath. - * @styleFormat: String representing the style of format (dump, save, snap= shot) - * @use_raw_on_fail: Boolean indicating how to handle the error path. For - * callers that are OK with invalid data or inability to - * find the compression program, just return a raw format - * and let the path remain as NULL. - * - * Returns: - * virQEMUSaveFormat - Integer representation of the compression - * program to be used for particular style - * (e.g. dump, save, or snapshot). - * QEMU_SAVE_FORMAT_RAW - If there is no qemu.conf imageFormat value or - * no there was an error, then just return RAW - * indicating none. - */ -static int ATTRIBUTE_NONNULL(2) -qemuGetCompressionProgram(const char *imageFormat, - char **compresspath, - const char *styleFormat, - bool use_raw_on_fail) -{ - int ret; - - *compresspath =3D NULL; - - if (!imageFormat) - return QEMU_SAVE_FORMAT_RAW; - - if ((ret =3D qemuSaveCompressionTypeFromString(imageFormat)) < 0) - goto error; - - if (ret =3D=3D QEMU_SAVE_FORMAT_RAW) - return QEMU_SAVE_FORMAT_RAW; - - if (!(*compresspath =3D virFindFileInPath(imageFormat))) - goto error; - - return ret; - - error: - if (ret < 0) { - if (use_raw_on_fail) - VIR_WARN("Invalid %s image format specified in " - "configuration file, using raw", - styleFormat); - else - virReportError(VIR_ERR_OPERATION_FAILED, - _("Invalid %s image format specified " - "in configuration file"), - styleFormat); - } else { - if (use_raw_on_fail) - VIR_WARN("Compression program for %s image format in " - "configuration file isn't available, using raw", - styleFormat); - else - virReportError(VIR_ERR_OPERATION_FAILED, - _("Compression program for %s image format " - "in configuration file isn't available"), - styleFormat); - } - - /* Use "raw" as the format if the specified format is not valid, - * or the compress program is not available. */ - if (use_raw_on_fail) - return QEMU_SAVE_FORMAT_RAW; - - return -1; -} - - static int qemuDomainSaveFlags(virDomainPtr dom, const char *path, const char *dxml, unsigned int flags) @@ -3761,147 +3440,6 @@ qemuDomainManagedSaveRemove(virDomainPtr dom, unsig= ned int flags) return ret; } =20 -static int qemuDumpToFd(virQEMUDriverPtr driver, virDomainObjPtr vm, - int fd, qemuDomainAsyncJob asyncJob, - const char *dumpformat) -{ - qemuDomainObjPrivatePtr priv =3D vm->privateData; - int ret =3D -1; - - if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_DUMP_GUEST_MEMORY)) { - virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", - _("dump-guest-memory is not supported")); - return -1; - } - - if (qemuSecuritySetImageFDLabel(driver->securityManager, vm->def, fd) = < 0) - return -1; - - VIR_FREE(priv->job.current); - priv->job.dump_memory_only =3D true; - - if (qemuDomainObjEnterMonitorAsync(driver, vm, asyncJob) < 0) - return -1; - - if (dumpformat) { - ret =3D qemuMonitorGetDumpGuestMemoryCapability(priv->mon, dumpfor= mat); - - if (ret <=3D 0) { - virReportError(VIR_ERR_INVALID_ARG, - _("unsupported dumpformat '%s' " - "for this QEMU binary"), - dumpformat); - ret =3D -1; - goto cleanup; - } - } - - ret =3D qemuMonitorDumpToFd(priv->mon, fd, dumpformat); - - cleanup: - ignore_value(qemuDomainObjExitMonitor(driver, vm)); - - return ret; -} - -static int -doCoreDump(virQEMUDriverPtr driver, - virDomainObjPtr vm, - const char *path, - unsigned int dump_flags, - unsigned int dumpformat) -{ - int fd =3D -1; - int ret =3D -1; - virFileWrapperFdPtr wrapperFd =3D NULL; - int directFlag =3D 0; - unsigned int flags =3D VIR_FILE_WRAPPER_NON_BLOCKING; - const char *memory_dump_format =3D NULL; - virQEMUDriverConfigPtr cfg =3D virQEMUDriverGetConfig(driver); - char *compressedpath =3D NULL; - - /* We reuse "save" flag for "dump" here. Then, we can support the same - * format in "save" and "dump". This path doesn't need the compression - * program to exist and can ignore the return value - it only cares to - * get the compressedpath */ - ignore_value(qemuGetCompressionProgram(cfg->dumpImageFormat, - &compressedpath, - "dump", true)); - - /* Create an empty file with appropriate ownership. */ - if (dump_flags & VIR_DUMP_BYPASS_CACHE) { - flags |=3D VIR_FILE_WRAPPER_BYPASS_CACHE; - directFlag =3D virFileDirectFdFlag(); - if (directFlag < 0) { - virReportError(VIR_ERR_OPERATION_FAILED, "%s", - _("bypass cache unsupported by this system")); - goto cleanup; - } - } - /* Core dumps usually imply last-ditch analysis efforts are - * desired, so we intentionally do not unlink even if a file was - * created. */ - if ((fd =3D qemuOpenFile(driver, vm, path, - O_CREAT | O_TRUNC | O_WRONLY | directFlag, - NULL, NULL)) < 0) - goto cleanup; - - if (!(wrapperFd =3D virFileWrapperFdNew(&fd, path, flags))) - goto cleanup; - - if (dump_flags & VIR_DUMP_MEMORY_ONLY) { - if (!(memory_dump_format =3D qemuDumpFormatTypeToString(dumpformat= ))) { - virReportError(VIR_ERR_INVALID_ARG, - _("unknown dumpformat '%d'"), dumpformat); - goto cleanup; - } - - /* qemu dumps in "elf" without dumpformat set */ - if (STREQ(memory_dump_format, "elf")) - memory_dump_format =3D NULL; - - ret =3D qemuDumpToFd(driver, vm, fd, QEMU_ASYNC_JOB_DUMP, - memory_dump_format); - } else { - if (dumpformat !=3D VIR_DOMAIN_CORE_DUMP_FORMAT_RAW) { - virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", - _("kdump-compressed format is only supported wi= th " - "memory-only dump")); - goto cleanup; - } - - if (!qemuMigrationIsAllowed(driver, vm, false, 0)) - goto cleanup; - - ret =3D qemuMigrationToFile(driver, vm, fd, compressedpath, - QEMU_ASYNC_JOB_DUMP); - } - - if (ret < 0) - goto cleanup; - - if (VIR_CLOSE(fd) < 0) { - virReportSystemError(errno, - _("unable to close file %s"), - path); - goto cleanup; - } - if (qemuFileWrapperFDClose(vm, wrapperFd) < 0) - goto cleanup; - - ret =3D 0; - - cleanup: - VIR_FORCE_CLOSE(fd); - if (ret !=3D 0) - unlink(path); - virFileWrapperFdFree(wrapperFd); - VIR_FREE(compressedpath); - virObjectUnref(cfg); - return ret; -} - - static int qemuDomainCoreDumpWithFormat(virDomainPtr dom, const char *path, @@ -4106,712 +3644,13 @@ qemuDomainScreenshot(virDomainPtr dom, return ret; } =20 -static char * -getAutoDumpPath(virQEMUDriverPtr driver, - virDomainObjPtr vm) -{ - char *dumpfile =3D NULL; - char *domname =3D virDomainObjGetShortName(vm->def); - char timestr[100]; - struct tm time_info; - time_t curtime =3D time(NULL); - virQEMUDriverConfigPtr cfg =3D NULL; - - if (!domname) - return NULL; - - cfg =3D virQEMUDriverGetConfig(driver); - - localtime_r(&curtime, &time_info); - strftime(timestr, sizeof(timestr), "%Y-%m-%d-%H:%M:%S", &time_info); - - ignore_value(virAsprintf(&dumpfile, "%s/%s-%s", - cfg->autoDumpPath, - domname, - timestr)); - - virObjectUnref(cfg); - VIR_FREE(domname); - return dumpfile; -} - -static void -processWatchdogEvent(virQEMUDriverPtr driver, - virDomainObjPtr vm, - int action) -{ - int ret; - virQEMUDriverConfigPtr cfg =3D virQEMUDriverGetConfig(driver); - char *dumpfile =3D getAutoDumpPath(driver, vm); - unsigned int flags =3D VIR_DUMP_MEMORY_ONLY; - - if (!dumpfile) - goto cleanup; - - switch (action) { - case VIR_DOMAIN_WATCHDOG_ACTION_DUMP: - if (qemuDomainObjBeginAsyncJob(driver, vm, - QEMU_ASYNC_JOB_DUMP, - VIR_DOMAIN_JOB_OPERATION_DUMP) < 0)= { - goto cleanup; - } - - if (!virDomainObjIsActive(vm)) { - virReportError(VIR_ERR_OPERATION_INVALID, - "%s", _("domain is not running")); - goto endjob; - } - - flags |=3D cfg->autoDumpBypassCache ? VIR_DUMP_BYPASS_CACHE: 0; - if ((ret =3D doCoreDump(driver, vm, dumpfile, flags, - VIR_DOMAIN_CORE_DUMP_FORMAT_RAW)) < 0) - virReportError(VIR_ERR_OPERATION_FAILED, - "%s", _("Dump failed")); - - ret =3D qemuProcessStartCPUs(driver, vm, NULL, - VIR_DOMAIN_RUNNING_UNPAUSED, - QEMU_ASYNC_JOB_DUMP); - - if (ret < 0) - virReportError(VIR_ERR_OPERATION_FAILED, - "%s", _("Resuming after dump failed")); - break; - default: - goto cleanup; - } - - endjob: - qemuDomainObjEndAsyncJob(driver, vm); - - cleanup: - VIR_FREE(dumpfile); - virObjectUnref(cfg); -} =20 -static int -doCoreDumpToAutoDumpPath(virQEMUDriverPtr driver, - virDomainObjPtr vm, - unsigned int flags) -{ - int ret =3D -1; - virQEMUDriverConfigPtr cfg =3D virQEMUDriverGetConfig(driver); - char *dumpfile =3D getAutoDumpPath(driver, vm); =20 - if (!dumpfile) - goto cleanup; =20 - flags |=3D cfg->autoDumpBypassCache ? VIR_DUMP_BYPASS_CACHE: 0; - if ((ret =3D doCoreDump(driver, vm, dumpfile, flags, - VIR_DOMAIN_CORE_DUMP_FORMAT_RAW)) < 0) - virReportError(VIR_ERR_OPERATION_FAILED, - "%s", _("Dump failed")); - cleanup: - VIR_FREE(dumpfile); - virObjectUnref(cfg); - return ret; -} =20 =20 -static void -qemuProcessGuestPanicEventInfo(virQEMUDriverPtr driver, - virDomainObjPtr vm, - qemuMonitorEventPanicInfoPtr info) -{ - char *msg =3D qemuMonitorGuestPanicEventInfoFormatMsg(info); - char *timestamp =3D virTimeStringNow(); =20 - if (msg && timestamp) - qemuDomainLogAppendMessage(driver, vm, "%s: panic %s\n", timestamp= , msg); =20 - VIR_FREE(timestamp); - VIR_FREE(msg); -} - - -static void -processGuestPanicEvent(virQEMUDriverPtr driver, - virDomainObjPtr vm, - int action, - qemuMonitorEventPanicInfoPtr info) -{ - qemuDomainObjPrivatePtr priv =3D vm->privateData; - virObjectEventPtr event =3D NULL; - virQEMUDriverConfigPtr cfg =3D virQEMUDriverGetConfig(driver); - bool removeInactive =3D false; - - if (qemuDomainObjBeginAsyncJob(driver, vm, QEMU_ASYNC_JOB_DUMP, - VIR_DOMAIN_JOB_OPERATION_DUMP) < 0) - goto cleanup; - - if (!virDomainObjIsActive(vm)) { - VIR_DEBUG("Ignoring GUEST_PANICKED event from inactive domain %s", - vm->def->name); - goto endjob; - } - - if (info) - qemuProcessGuestPanicEventInfo(driver, vm, info); - - virDomainObjSetState(vm, VIR_DOMAIN_CRASHED, VIR_DOMAIN_CRASHED_PANICK= ED); - - event =3D virDomainEventLifecycleNewFromObj(vm, - VIR_DOMAIN_EVENT_CRASHED, - VIR_DOMAIN_EVENT_CRASHED_PAN= ICKED); - - qemuDomainEventQueue(driver, event); - - if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm, driver->cap= s) < 0) { - VIR_WARN("Unable to save status on vm %s after state change", - vm->def->name); - } - - if (virDomainLockProcessPause(driver->lockManager, vm, &priv->lockStat= e) < 0) - VIR_WARN("Unable to release lease on %s", vm->def->name); - VIR_DEBUG("Preserving lock state '%s'", NULLSTR(priv->lockState)); - - switch (action) { - case VIR_DOMAIN_LIFECYCLE_CRASH_COREDUMP_DESTROY: - if (doCoreDumpToAutoDumpPath(driver, vm, VIR_DUMP_MEMORY_ONLY) < 0) - goto endjob; - ATTRIBUTE_FALLTHROUGH; - - case VIR_DOMAIN_LIFECYCLE_CRASH_DESTROY: - qemuProcessStop(driver, vm, VIR_DOMAIN_SHUTOFF_CRASHED, - QEMU_ASYNC_JOB_DUMP, 0); - event =3D virDomainEventLifecycleNewFromObj(vm, - VIR_DOMAIN_EVENT_STOPPED, - VIR_DOMAIN_EVENT_STOPPED= _CRASHED); - - qemuDomainEventQueue(driver, event); - virDomainAuditStop(vm, "destroyed"); - removeInactive =3D true; - break; - - case VIR_DOMAIN_LIFECYCLE_CRASH_COREDUMP_RESTART: - if (doCoreDumpToAutoDumpPath(driver, vm, VIR_DUMP_MEMORY_ONLY) < 0) - goto endjob; - ATTRIBUTE_FALLTHROUGH; - - case VIR_DOMAIN_LIFECYCLE_CRASH_RESTART: - qemuDomainSetFakeReboot(driver, vm, true); - qemuProcessShutdownOrReboot(driver, vm); - break; - - case VIR_DOMAIN_LIFECYCLE_CRASH_PRESERVE: - break; - - default: - break; - } - - endjob: - qemuDomainObjEndAsyncJob(driver, vm); - if (removeInactive) - qemuDomainRemoveInactiveJob(driver, vm); - - cleanup: - virObjectUnref(cfg); -} - - -static void -processDeviceDeletedEvent(virQEMUDriverPtr driver, - virDomainObjPtr vm, - char *devAlias) -{ - virQEMUDriverConfigPtr cfg =3D virQEMUDriverGetConfig(driver); - virDomainDeviceDef dev; - - VIR_DEBUG("Removing device %s from domain %p %s", - devAlias, vm, vm->def->name); - - if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0) - goto cleanup; - - if (!virDomainObjIsActive(vm)) { - VIR_DEBUG("Domain is not running"); - goto endjob; - } - - if (STRPREFIX(devAlias, "vcpu")) { - qemuDomainRemoveVcpuAlias(driver, vm, devAlias); - } else { - if (virDomainDefFindDevice(vm->def, devAlias, &dev, true) < 0) - goto endjob; - - if (qemuDomainRemoveDevice(driver, vm, &dev) < 0) - goto endjob; - } - - if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm, driver->cap= s) < 0) - VIR_WARN("unable to save domain status after removing device %s", - devAlias); - - endjob: - qemuDomainObjEndJob(driver, vm); - - cleanup: - VIR_FREE(devAlias); - virObjectUnref(cfg); -} - - -static void -syncNicRxFilterMacAddr(char *ifname, virNetDevRxFilterPtr guestFilter, - virNetDevRxFilterPtr hostFilter) -{ - char newMacStr[VIR_MAC_STRING_BUFLEN]; - - if (virMacAddrCmp(&hostFilter->mac, &guestFilter->mac)) { - virMacAddrFormat(&guestFilter->mac, newMacStr); - - /* set new MAC address from guest to associated macvtap device */ - if (virNetDevSetMAC(ifname, &guestFilter->mac) < 0) { - VIR_WARN("Couldn't set new MAC address %s to device %s " - "while responding to NIC_RX_FILTER_CHANGED", - newMacStr, ifname); - } else { - VIR_DEBUG("device %s MAC address set to %s", ifname, newMacStr= ); - } - } -} - - -static void -syncNicRxFilterGuestMulticast(char *ifname, virNetDevRxFilterPtr guestFilt= er, - virNetDevRxFilterPtr hostFilter) -{ - size_t i, j; - bool found; - char macstr[VIR_MAC_STRING_BUFLEN]; - - for (i =3D 0; i < guestFilter->multicast.nTable; i++) { - found =3D false; - - for (j =3D 0; j < hostFilter->multicast.nTable; j++) { - if (virMacAddrCmp(&guestFilter->multicast.table[i], - &hostFilter->multicast.table[j]) =3D=3D 0) { - found =3D true; - break; - } - } - - if (!found) { - virMacAddrFormat(&guestFilter->multicast.table[i], macstr); - - if (virNetDevAddMulti(ifname, &guestFilter->multicast.table[i]= ) < 0) { - VIR_WARN("Couldn't add new multicast MAC address %s to " - "device %s while responding to NIC_RX_FILTER_CHAN= GED", - macstr, ifname); - } else { - VIR_DEBUG("Added multicast MAC %s to %s interface", - macstr, ifname); - } - } - } -} - - -static void -syncNicRxFilterHostMulticast(char *ifname, virNetDevRxFilterPtr guestFilte= r, - virNetDevRxFilterPtr hostFilter) -{ - size_t i, j; - bool found; - char macstr[VIR_MAC_STRING_BUFLEN]; - - for (i =3D 0; i < hostFilter->multicast.nTable; i++) { - found =3D false; - - for (j =3D 0; j < guestFilter->multicast.nTable; j++) { - if (virMacAddrCmp(&hostFilter->multicast.table[i], - &guestFilter->multicast.table[j]) =3D=3D 0) { - found =3D true; - break; - } - } - - if (!found) { - virMacAddrFormat(&hostFilter->multicast.table[i], macstr); - - if (virNetDevDelMulti(ifname, &hostFilter->multicast.table[i])= < 0) { - VIR_WARN("Couldn't delete multicast MAC address %s from " - "device %s while responding to NIC_RX_FILTER_CHAN= GED", - macstr, ifname); - } else { - VIR_DEBUG("Deleted multicast MAC %s from %s interface", - macstr, ifname); - } - } - } -} - - -static void -syncNicRxFilterPromiscMode(char *ifname, - virNetDevRxFilterPtr guestFilter, - virNetDevRxFilterPtr hostFilter) -{ - bool promisc; - bool setpromisc =3D false; - - /* Set macvtap promisc mode to true if the guest has vlans defined */ - /* or synchronize the macvtap promisc mode if different from guest */ - if (guestFilter->vlan.nTable > 0) { - if (!hostFilter->promiscuous) { - setpromisc =3D true; - promisc =3D true; - } - } else if (hostFilter->promiscuous !=3D guestFilter->promiscuous) { - setpromisc =3D true; - promisc =3D guestFilter->promiscuous; - } - - if (setpromisc) { - if (virNetDevSetPromiscuous(ifname, promisc) < 0) { - VIR_WARN("Couldn't set PROMISC flag to %s for device %s " - "while responding to NIC_RX_FILTER_CHANGED", - promisc ? "true" : "false", ifname); - } - } -} - - -static void -syncNicRxFilterMultiMode(char *ifname, virNetDevRxFilterPtr guestFilter, - virNetDevRxFilterPtr hostFilter) -{ - if (hostFilter->multicast.mode !=3D guestFilter->multicast.mode) { - switch (guestFilter->multicast.mode) { - case VIR_NETDEV_RX_FILTER_MODE_ALL: - if (virNetDevSetRcvAllMulti(ifname, true)) { - - VIR_WARN("Couldn't set allmulticast flag to 'on' for " - "device %s while responding to " - "NIC_RX_FILTER_CHANGED", ifname); - } - break; - - case VIR_NETDEV_RX_FILTER_MODE_NORMAL: - if (virNetDevSetRcvMulti(ifname, true)) { - - VIR_WARN("Couldn't set multicast flag to 'on' for " - "device %s while responding to " - "NIC_RX_FILTER_CHANGED", ifname); - } - - if (virNetDevSetRcvAllMulti(ifname, false)) { - VIR_WARN("Couldn't set allmulticast flag to 'off' for " - "device %s while responding to " - "NIC_RX_FILTER_CHANGED", ifname); - } - break; - - case VIR_NETDEV_RX_FILTER_MODE_NONE: - if (virNetDevSetRcvAllMulti(ifname, false)) { - VIR_WARN("Couldn't set allmulticast flag to 'off' for " - "device %s while responding to " - "NIC_RX_FILTER_CHANGED", ifname); - } - - if (virNetDevSetRcvMulti(ifname, false)) { - VIR_WARN("Couldn't set multicast flag to 'off' for " - "device %s while responding to " - "NIC_RX_FILTER_CHANGED", - ifname); - } - break; - } - } -} - - -static void -syncNicRxFilterDeviceOptions(char *ifname, virNetDevRxFilterPtr guestFilte= r, - virNetDevRxFilterPtr hostFilter) -{ - syncNicRxFilterPromiscMode(ifname, guestFilter, hostFilter); - syncNicRxFilterMultiMode(ifname, guestFilter, hostFilter); -} - - -static void -syncNicRxFilterMulticast(char *ifname, - virNetDevRxFilterPtr guestFilter, - virNetDevRxFilterPtr hostFilter) -{ - syncNicRxFilterGuestMulticast(ifname, guestFilter, hostFilter); - syncNicRxFilterHostMulticast(ifname, guestFilter, hostFilter); -} - -static void -processNicRxFilterChangedEvent(virQEMUDriverPtr driver, - virDomainObjPtr vm, - char *devAlias) -{ - virQEMUDriverConfigPtr cfg =3D virQEMUDriverGetConfig(driver); - qemuDomainObjPrivatePtr priv =3D vm->privateData; - virDomainDeviceDef dev; - virDomainNetDefPtr def; - virNetDevRxFilterPtr guestFilter =3D NULL; - virNetDevRxFilterPtr hostFilter =3D NULL; - int ret; - - VIR_DEBUG("Received NIC_RX_FILTER_CHANGED event for device %s " - "from domain %p %s", - devAlias, vm, vm->def->name); - - if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0) - goto cleanup; - - if (!virDomainObjIsActive(vm)) { - VIR_DEBUG("Domain is not running"); - goto endjob; - } - - if (virDomainDefFindDevice(vm->def, devAlias, &dev, true) < 0) { - VIR_WARN("NIC_RX_FILTER_CHANGED event received for " - "non-existent device %s in domain %s", - devAlias, vm->def->name); - goto endjob; - } - if (dev.type !=3D VIR_DOMAIN_DEVICE_NET) { - VIR_WARN("NIC_RX_FILTER_CHANGED event received for " - "non-network device %s in domain %s", - devAlias, vm->def->name); - goto endjob; - } - def =3D dev.data.net; - - if (!virDomainNetGetActualTrustGuestRxFilters(def)) { - VIR_DEBUG("ignore NIC_RX_FILTER_CHANGED event for network " - "device %s in domain %s", - def->info.alias, vm->def->name); - /* not sending "query-rx-filter" will also suppress any - * further NIC_RX_FILTER_CHANGED events for this device - */ - goto endjob; - } - - /* handle the event - send query-rx-filter and respond to it. */ - - VIR_DEBUG("process NIC_RX_FILTER_CHANGED event for network " - "device %s in domain %s", def->info.alias, vm->def->name); - - qemuDomainObjEnterMonitor(driver, vm); - ret =3D qemuMonitorQueryRxFilter(priv->mon, devAlias, &guestFilter); - if (qemuDomainObjExitMonitor(driver, vm) < 0) - ret =3D -1; - if (ret < 0) - goto endjob; - - if (virDomainNetGetActualType(def) =3D=3D VIR_DOMAIN_NET_TYPE_DIRECT) { - - if (virNetDevGetRxFilter(def->ifname, &hostFilter)) { - VIR_WARN("Couldn't get current RX filter for device %s " - "while responding to NIC_RX_FILTER_CHANGED", - def->ifname); - goto endjob; - } - - /* For macvtap connections, set the following macvtap network devi= ce - * attributes to match those of the guest network device: - * - MAC address - * - Multicast MAC address table - * - Device options: - * - PROMISC - * - MULTICAST - * - ALLMULTI - */ - syncNicRxFilterMacAddr(def->ifname, guestFilter, hostFilter); - syncNicRxFilterMulticast(def->ifname, guestFilter, hostFilter); - syncNicRxFilterDeviceOptions(def->ifname, guestFilter, hostFilter); - } - - if (virDomainNetGetActualType(def) =3D=3D VIR_DOMAIN_NET_TYPE_NETWORK)= { - const char *brname =3D virDomainNetGetActualBridgeName(def); - - /* For libivrt network connections, set the following TUN/TAP netw= ork - * device attributes to match those of the guest network device: - * - QoS filters (which are based on MAC address) - */ - if (virDomainNetGetActualBandwidth(def) && - def->data.network.actual && - virNetDevBandwidthUpdateFilter(brname, &guestFilter->mac, - def->data.network.actual->class= _id) < 0) - goto endjob; - } - - endjob: - qemuDomainObjEndJob(driver, vm); - - cleanup: - virNetDevRxFilterFree(hostFilter); - virNetDevRxFilterFree(guestFilter); - VIR_FREE(devAlias); - virObjectUnref(cfg); -} - - -static void -processSerialChangedEvent(virQEMUDriverPtr driver, - virDomainObjPtr vm, - char *devAlias, - bool connected) -{ - virQEMUDriverConfigPtr cfg =3D virQEMUDriverGetConfig(driver); - virDomainChrDeviceState newstate; - virObjectEventPtr event =3D NULL; - virDomainDeviceDef dev; - qemuDomainObjPrivatePtr priv =3D vm->privateData; - - if (connected) - newstate =3D VIR_DOMAIN_CHR_DEVICE_STATE_CONNECTED; - else - newstate =3D VIR_DOMAIN_CHR_DEVICE_STATE_DISCONNECTED; - - VIR_DEBUG("Changing serial port state %s in domain %p %s", - devAlias, vm, vm->def->name); - - if (newstate =3D=3D VIR_DOMAIN_CHR_DEVICE_STATE_DISCONNECTED && - virDomainObjIsActive(vm) && priv->agent) { - /* peek into the domain definition to find the channel */ - if (virDomainDefFindDevice(vm->def, devAlias, &dev, true) =3D=3D 0= && - dev.type =3D=3D VIR_DOMAIN_DEVICE_CHR && - dev.data.chr->deviceType =3D=3D VIR_DOMAIN_CHR_DEVICE_TYPE_CHA= NNEL && - dev.data.chr->targetType =3D=3D VIR_DOMAIN_CHR_CHANNEL_TARGET_= TYPE_VIRTIO && - STREQ_NULLABLE(dev.data.chr->target.name, "org.qemu.guest_agen= t.0")) - /* Close agent monitor early, so that other threads - * waiting for the agent to reply can finish and our - * job we acquire below can succeed. */ - qemuAgentNotifyClose(priv->agent); - - /* now discard the data, since it may possibly change once we unlo= ck - * while entering the job */ - memset(&dev, 0, sizeof(dev)); - } - - if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0) - goto cleanup; - - if (!virDomainObjIsActive(vm)) { - VIR_DEBUG("Domain is not running"); - goto endjob; - } - - if (virDomainDefFindDevice(vm->def, devAlias, &dev, true) < 0) - goto endjob; - - /* we care only about certain devices */ - if (dev.type !=3D VIR_DOMAIN_DEVICE_CHR || - dev.data.chr->deviceType !=3D VIR_DOMAIN_CHR_DEVICE_TYPE_CHANNEL || - dev.data.chr->targetType !=3D VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_V= IRTIO) - goto endjob; - - dev.data.chr->state =3D newstate; - - if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm, driver->cap= s) < 0) - VIR_WARN("unable to save status of domain %s after updating state = of " - "channel %s", vm->def->name, devAlias); - - if (STREQ_NULLABLE(dev.data.chr->target.name, "org.qemu.guest_agent.0"= )) { - if (newstate =3D=3D VIR_DOMAIN_CHR_DEVICE_STATE_CONNECTED) { - if (qemuConnectAgent(driver, vm) < 0) - goto endjob; - } else { - if (priv->agent) { - qemuAgentClose(priv->agent); - priv->agent =3D NULL; - } - priv->agentError =3D false; - } - - event =3D virDomainEventAgentLifecycleNewFromObj(vm, newstate, - VIR_CONNECT_DOMAIN_= EVENT_AGENT_LIFECYCLE_REASON_CHANNEL); - qemuDomainEventQueue(driver, event); - } - - endjob: - qemuDomainObjEndJob(driver, vm); - - cleanup: - VIR_FREE(devAlias); - virObjectUnref(cfg); - -} - - -static void -processBlockJobEvent(virQEMUDriverPtr driver, - virDomainObjPtr vm, - char *diskAlias, - int type, - int status) -{ - virDomainDiskDefPtr disk; - - if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0) - goto cleanup; - - if (!virDomainObjIsActive(vm)) { - VIR_DEBUG("Domain is not running"); - goto endjob; - } - - if ((disk =3D qemuProcessFindDomainDiskByAlias(vm, diskAlias))) - qemuBlockJobEventProcess(driver, vm, disk, QEMU_ASYNC_JOB_NONE, ty= pe, status); - - endjob: - qemuDomainObjEndJob(driver, vm); - cleanup: - VIR_FREE(diskAlias); -} - - -static void -processMonitorEOFEvent(virQEMUDriverPtr driver, - virDomainObjPtr vm) -{ - qemuDomainObjPrivatePtr priv =3D vm->privateData; - int eventReason =3D VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN; - int stopReason =3D VIR_DOMAIN_SHUTOFF_SHUTDOWN; - const char *auditReason =3D "shutdown"; - unsigned int stopFlags =3D 0; - virObjectEventPtr event =3D NULL; - - if (qemuProcessBeginStopJob(driver, vm, QEMU_JOB_DESTROY, true) < 0) - return; - - if (!virDomainObjIsActive(vm)) { - VIR_DEBUG("Domain %p '%s' is not active, ignoring EOF", - vm, vm->def->name); - goto endjob; - } - - if (priv->monJSON && !priv->gotShutdown) { - VIR_DEBUG("Monitor connection to '%s' closed without SHUTDOWN even= t; " - "assuming the domain crashed", vm->def->name); - eventReason =3D VIR_DOMAIN_EVENT_STOPPED_FAILED; - stopReason =3D VIR_DOMAIN_SHUTOFF_CRASHED; - auditReason =3D "failed"; - } - - if (priv->job.asyncJob =3D=3D QEMU_ASYNC_JOB_MIGRATION_IN) { - stopFlags |=3D VIR_QEMU_PROCESS_STOP_MIGRATED; - qemuMigrationErrorSave(driver, vm->def->name, - qemuMonitorLastError(priv->mon)); - } - - event =3D virDomainEventLifecycleNewFromObj(vm, VIR_DOMAIN_EVENT_STOPP= ED, - eventReason); - qemuProcessStop(driver, vm, stopReason, QEMU_ASYNC_JOB_NONE, stopFlags= ); - virDomainAuditStop(vm, auditReason); - qemuDomainEventQueue(driver, event); - - endjob: - qemuDomainRemoveInactive(driver, vm); - qemuDomainObjEndJob(driver, vm); -} =20 =20 static void qemuProcessEventHandler(void *data, void *opaque) diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index ee8bae5..d2b5fe8 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -36,6 +36,7 @@ #include "qemu_processpriv.h" #include "qemu_alias.h" #include "qemu_block.h" +#include "qemu_blockjob.h" #include "qemu_domain.h" #include "qemu_domain_address.h" #include "qemu_cgroup.h" @@ -87,6 +88,18 @@ typedef struct { void (*handler_func)(qemuEventPtr ev, void *opaque); } qemuEventFuncTable; =20 +VIR_ENUM_IMPL(qemuSaveCompression, QEMU_SAVE_FORMAT_LAST, + "raw", + "gzip", + "bzip2", + "xz", + "lzop") + +VIR_ENUM_IMPL(qemuDumpFormat, VIR_DOMAIN_CORE_DUMP_FORMAT_LAST, + "elf", + "kdump-zlib", + "kdump-lzo", + "kdump-snappy") =20 /** * qemuProcessRemoveDomainStatus @@ -7458,3 +7471,1123 @@ qemuProcessReconnectAll(virConnectPtr conn, virQEM= UDriverPtr driver) struct qemuProcessReconnectData data =3D {.conn =3D conn, .driver =3D = driver}; virDomainObjListForEach(driver->domains, qemuProcessReconnectHelper, &= data); } + +static int +qemuOpenFileAs(uid_t fallback_uid, gid_t fallback_gid, + bool dynamicOwnership, + const char *path, int oflags, + bool *needUnlink, bool *bypassSecurityDriver) +{ + struct stat sb; + bool is_reg =3D true; + bool need_unlink =3D false; + bool bypass_security =3D false; + unsigned int vfoflags =3D 0; + int fd =3D -1; + int path_shared =3D virFileIsSharedFS(path); + uid_t uid =3D geteuid(); + gid_t gid =3D getegid(); + + /* path might be a pre-existing block dev, in which case + * we need to skip the create step, and also avoid unlink + * in the failure case */ + if (oflags & O_CREAT) { + need_unlink =3D true; + + /* Don't force chown on network-shared FS + * as it is likely to fail. */ + if (path_shared <=3D 0 || dynamicOwnership) + vfoflags |=3D VIR_FILE_OPEN_FORCE_OWNER; + + if (stat(path, &sb) =3D=3D 0) { + /* It already exists, we don't want to delete it on error */ + need_unlink =3D false; + + is_reg =3D !!S_ISREG(sb.st_mode); + /* If the path is regular file which exists + * already and dynamic_ownership is off, we don't + * want to change its ownership, just open it as-is */ + if (is_reg && !dynamicOwnership) { + uid =3D sb.st_uid; + gid =3D sb.st_gid; + } + } + } + + /* First try creating the file as root */ + if (!is_reg) { + if ((fd =3D open(path, oflags & ~O_CREAT)) < 0) { + fd =3D -errno; + goto error; + } + } else { + if ((fd =3D virFileOpenAs(path, oflags, S_IRUSR | S_IWUSR, uid, gi= d, + vfoflags | VIR_FILE_OPEN_NOFORK)) < 0) { + /* If we failed as root, and the error was permission-denied + (EACCES or EPERM), assume it's on a network-connected share + where root access is restricted (eg, root-squashed NFS). If= the + qemu user is non-root, just set a flag to + bypass security driver shenanigans, and retry the operation + after doing setuid to qemu user */ + if ((fd !=3D -EACCES && fd !=3D -EPERM) || fallback_uid =3D=3D= geteuid()) + goto error; + + /* On Linux we can also verify the FS-type of the directory. */ + switch (path_shared) { + case 1: + /* it was on a network share, so we'll continue + * as outlined above + */ + break; + + case -1: + virReportSystemError(-fd, oflags & O_CREAT + ? _("Failed to create file " + "'%s': couldn't determine fs = type") + : _("Failed to open file " + "'%s': couldn't determine fs = type"), + path); + goto cleanup; + + case 0: + default: + /* local file - log the error returned by virFileOpenA= s */ + goto error; + } + + /* If we created the file above, then we need to remove it; + * otherwise, the next attempt to create will fail. If the + * file had already existed before we got here, then we also + * don't want to delete it and allow the following to succeed + * or fail based on existing protections + */ + if (need_unlink) + unlink(path); + + /* Retry creating the file as qemu user */ + + /* Since we're passing different modes... */ + vfoflags |=3D VIR_FILE_OPEN_FORCE_MODE; + + if ((fd =3D virFileOpenAs(path, oflags, + S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP, + fallback_uid, fallback_gid, + vfoflags | VIR_FILE_OPEN_FORK)) < 0) { + virReportSystemError(-fd, oflags & O_CREAT + ? _("Error from child process creatin= g '%s'") + : _("Error from child process opening= '%s'"), + path); + goto cleanup; + } + + /* Since we had to setuid to create the file, and the fstype + is NFS, we assume it's a root-squashing NFS share, and that + the security driver stuff would have failed anyway */ + + bypass_security =3D true; + } + } + cleanup: + if (needUnlink) + *needUnlink =3D need_unlink; + if (bypassSecurityDriver) + *bypassSecurityDriver =3D bypass_security; + return fd; + + error: + virReportSystemError(-fd, oflags & O_CREAT + ? _("Failed to create file '%s'") + : _("Failed to open file '%s'"), + path); + goto cleanup; +} + +/** + * qemuOpenFile: + * @driver: driver object + * @vm: domain object + * @path: path to file to open + * @oflags: flags for opening/creation of the file + * @needUnlink: set to true if file was created by this function + * @bypassSecurityDriver: optional pointer to a boolean that will be set t= o true + * if security driver operations are pointless (due= to + * NFS mount) + * + * Internal function to properly create or open existing files, with + * ownership affected by qemu driver setup and domain DAC label. + * + * Returns the file descriptor on success and negative errno on failure. + * + * This function should not be used on storage sources. Use + * qemuDomainStorageFileInit and storage driver APIs if possible. + **/ +int +qemuOpenFile(virQEMUDriverPtr driver, + virDomainObjPtr vm, + const char *path, + int oflags, + bool *needUnlink, + bool *bypassSecurityDriver) +{ + int ret =3D -1; + virQEMUDriverConfigPtr cfg =3D virQEMUDriverGetConfig(driver); + uid_t user =3D cfg->user; + gid_t group =3D cfg->group; + bool dynamicOwnership =3D cfg->dynamicOwnership; + virSecurityLabelDefPtr seclabel; + + virObjectUnref(cfg); + + /* TODO: Take imagelabel into account? */ + if (vm && + (seclabel =3D virDomainDefGetSecurityLabelDef(vm->def, "dac")) != =3D NULL && + seclabel->label !=3D NULL && + (virParseOwnershipIds(seclabel->label, &user, &group) < 0)) + goto cleanup; + + ret =3D qemuOpenFileAs(user, group, dynamicOwnership, + path, oflags, needUnlink, bypassSecurityDriver); + + cleanup: + return ret; +} + +/* qemuGetCompressionProgram: + * @imageFormat: String representation from qemu.conf for the compression + * image format being used (dump, save, or snapshot). + * @compresspath: Pointer to a character string to store the fully qualifi= ed + * path from virFindFileInPath. + * @styleFormat: String representing the style of format (dump, save, snap= shot) + * @use_raw_on_fail: Boolean indicating how to handle the error path. For + * callers that are OK with invalid data or inability to + * find the compression program, just return a raw format + * and let the path remain as NULL. + * + * Returns: + * virQEMUSaveFormat - Integer representation of the compression + * program to be used for particular style + * (e.g. dump, save, or snapshot). + * QEMU_SAVE_FORMAT_RAW - If there is no qemu.conf imageFormat value or + * no there was an error, then just return RAW + * indicating none. + **/ +int ATTRIBUTE_NONNULL(2) +qemuGetCompressionProgram(const char *imageFormat, + char **compresspath, + const char *styleFormat, + bool use_raw_on_fail) +{ + int ret; + + *compresspath =3D NULL; + + if (!imageFormat) + return QEMU_SAVE_FORMAT_RAW; + + if ((ret =3D qemuSaveCompressionTypeFromString(imageFormat)) < 0) + goto error; + + if (ret =3D=3D QEMU_SAVE_FORMAT_RAW) + return QEMU_SAVE_FORMAT_RAW; + + if (!(*compresspath =3D virFindFileInPath(imageFormat))) + goto error; + + return ret; + + error: + if (ret < 0) { + if (use_raw_on_fail) + VIR_WARN("Invalid %s image format specified in " + "configuration file, using raw", + styleFormat); + else + virReportError(VIR_ERR_OPERATION_FAILED, + _("Invalid %s image format specified " + "in configuration file"), + styleFormat); + } else { + if (use_raw_on_fail) + VIR_WARN("Compression program for %s image format in " + "configuration file isn't available, using raw", + styleFormat); + else + virReportError(VIR_ERR_OPERATION_FAILED, + _("Compression program for %s image format " + "in configuration file isn't available"), + styleFormat); + } + + /* Use "raw" as the format if the specified format is not valid, + * or the compress program is not available. */ + if (use_raw_on_fail) + return QEMU_SAVE_FORMAT_RAW; + + return -1; +} + + +static int qemuDumpToFd(virQEMUDriverPtr driver, virDomainObjPtr vm, + int fd, qemuDomainAsyncJob asyncJob, + const char *dumpformat) +{ + qemuDomainObjPrivatePtr priv =3D vm->privateData; + int ret =3D -1; + + if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_DUMP_GUEST_MEMORY)) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", + _("dump-guest-memory is not supported")); + return -1; + } + + if (qemuSecuritySetImageFDLabel(driver->securityManager, vm->def, fd) = < 0) + return -1; + + VIR_FREE(priv->job.current); + priv->job.dump_memory_only =3D true; + + if (qemuDomainObjEnterMonitorAsync(driver, vm, asyncJob) < 0) + return -1; + + if (dumpformat) { + ret =3D qemuMonitorGetDumpGuestMemoryCapability(priv->mon, dumpfor= mat); + + if (ret <=3D 0) { + virReportError(VIR_ERR_INVALID_ARG, + _("unsupported dumpformat '%s' " + "for this QEMU binary"), + dumpformat); + ret =3D -1; + goto cleanup; + } + } + + ret =3D qemuMonitorDumpToFd(priv->mon, fd, dumpformat); + + cleanup: + ignore_value(qemuDomainObjExitMonitor(driver, vm)); + + return ret; +} + +int +doCoreDump(virQEMUDriverPtr driver, + virDomainObjPtr vm, + const char *path, + unsigned int dump_flags, + unsigned int dumpformat) +{ + int fd =3D -1; + int ret =3D -1; + virFileWrapperFdPtr wrapperFd =3D NULL; + int directFlag =3D 0; + unsigned int flags =3D VIR_FILE_WRAPPER_NON_BLOCKING; + const char *memory_dump_format =3D NULL; + virQEMUDriverConfigPtr cfg =3D virQEMUDriverGetConfig(driver); + char *compressedpath =3D NULL; + + /* We reuse "save" flag for "dump" here. Then, we can support the same + * format in "save" and "dump". This path doesn't need the compression + * program to exist and can ignore the return value - it only cares to + * get the compressedpath */ + ignore_value(qemuGetCompressionProgram(cfg->dumpImageFormat, + &compressedpath, + "dump", true)); + + /* Create an empty file with appropriate ownership. */ + if (dump_flags & VIR_DUMP_BYPASS_CACHE) { + flags |=3D VIR_FILE_WRAPPER_BYPASS_CACHE; + directFlag =3D virFileDirectFdFlag(); + if (directFlag < 0) { + virReportError(VIR_ERR_OPERATION_FAILED, "%s", + _("bypass cache unsupported by this system")); + goto cleanup; + } + } + /* Core dumps usually imply last-ditch analysis efforts are + * desired, so we intentionally do not unlink even if a file was + * created. */ + if ((fd =3D qemuOpenFile(driver, vm, path, + O_CREAT | O_TRUNC | O_WRONLY | directFlag, + NULL, NULL)) < 0) + goto cleanup; + + if (!(wrapperFd =3D virFileWrapperFdNew(&fd, path, flags))) + goto cleanup; + + if (dump_flags & VIR_DUMP_MEMORY_ONLY) { + if (!(memory_dump_format =3D qemuDumpFormatTypeToString(dumpformat= ))) { + virReportError(VIR_ERR_INVALID_ARG, + _("unknown dumpformat '%d'"), dumpformat); + goto cleanup; + } + + /* qemu dumps in "elf" without dumpformat set */ + if (STREQ(memory_dump_format, "elf")) + memory_dump_format =3D NULL; + + ret =3D qemuDumpToFd(driver, vm, fd, QEMU_ASYNC_JOB_DUMP, + memory_dump_format); + } else { + if (dumpformat !=3D VIR_DOMAIN_CORE_DUMP_FORMAT_RAW) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", + _("kdump-compressed format is only supported wi= th " + "memory-only dump")); + goto cleanup; + } + + if (!qemuMigrationIsAllowed(driver, vm, false, 0)) + goto cleanup; + + ret =3D qemuMigrationToFile(driver, vm, fd, compressedpath, + QEMU_ASYNC_JOB_DUMP); + } + + if (ret < 0) + goto cleanup; + + if (VIR_CLOSE(fd) < 0) { + virReportSystemError(errno, + _("unable to close file %s"), + path); + goto cleanup; + } + if (qemuFileWrapperFDClose(vm, wrapperFd) < 0) + goto cleanup; + + ret =3D 0; + + cleanup: + VIR_FORCE_CLOSE(fd); + if (ret !=3D 0) + unlink(path); + virFileWrapperFdFree(wrapperFd); + VIR_FREE(compressedpath); + virObjectUnref(cfg); + return ret; +} + +int +qemuFileWrapperFDClose(virDomainObjPtr vm, + virFileWrapperFdPtr fd) +{ + int ret; + + /* virFileWrapperFd uses iohelper to write data onto disk. + * However, iohelper calls fdatasync() which may take ages to + * finish. Therefore, we shouldn't be waiting with the domain + * object locked. */ + + /* XXX Currently, this function is intended for *Save() only + * as restore needs some reworking before it's ready for + * this. */ + + virObjectUnlock(vm); + ret =3D virFileWrapperFdClose(fd); + virObjectLock(vm); + if (!virDomainObjIsActive(vm)) { + if (!virGetLastError()) + virReportError(VIR_ERR_OPERATION_FAILED, "%s", + _("domain is no longer running")); + ret =3D -1; + } + return ret; +} + +static char * +getAutoDumpPath(virQEMUDriverPtr driver, + virDomainObjPtr vm) +{ + char *dumpfile =3D NULL; + char *domname =3D virDomainObjGetShortName(vm->def); + char timestr[100]; + struct tm time_info; + time_t curtime =3D time(NULL); + virQEMUDriverConfigPtr cfg =3D NULL; + + if (!domname) + return NULL; + + cfg =3D virQEMUDriverGetConfig(driver); + + localtime_r(&curtime, &time_info); + strftime(timestr, sizeof(timestr), "%Y-%m-%d-%H:%M:%S", &time_info); + + ignore_value(virAsprintf(&dumpfile, "%s/%s-%s", + cfg->autoDumpPath, + domname, + timestr)); + + virObjectUnref(cfg); + VIR_FREE(domname); + return dumpfile; +} +void +processWatchdogEvent(virQEMUDriverPtr driver, + virDomainObjPtr vm, + int action) +{ + int ret; + virQEMUDriverConfigPtr cfg =3D virQEMUDriverGetConfig(driver); + char *dumpfile =3D getAutoDumpPath(driver, vm); + unsigned int flags =3D VIR_DUMP_MEMORY_ONLY; + + if (!dumpfile) + goto cleanup; + + switch (action) { + case VIR_DOMAIN_WATCHDOG_ACTION_DUMP: + if (qemuDomainObjBeginAsyncJob(driver, vm, + QEMU_ASYNC_JOB_DUMP, + VIR_DOMAIN_JOB_OPERATION_DUMP) < 0)= { + goto cleanup; + } + + if (!virDomainObjIsActive(vm)) { + virReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("domain is not running")); + goto endjob; + } + + flags |=3D cfg->autoDumpBypassCache ? VIR_DUMP_BYPASS_CACHE: 0; + if ((ret =3D doCoreDump(driver, vm, dumpfile, flags, + VIR_DOMAIN_CORE_DUMP_FORMAT_RAW)) < 0) + virReportError(VIR_ERR_OPERATION_FAILED, + "%s", _("Dump failed")); + + ret =3D qemuProcessStartCPUs(driver, vm, NULL, + VIR_DOMAIN_RUNNING_UNPAUSED, + QEMU_ASYNC_JOB_DUMP); + + if (ret < 0) + virReportError(VIR_ERR_OPERATION_FAILED, + "%s", _("Resuming after dump failed")); + break; + default: + goto cleanup; + } + + endjob: + qemuDomainObjEndAsyncJob(driver, vm); + + cleanup: + VIR_FREE(dumpfile); + virObjectUnref(cfg); +} + +static int +doCoreDumpToAutoDumpPath(virQEMUDriverPtr driver, + virDomainObjPtr vm, + unsigned int flags) +{ + int ret =3D -1; + virQEMUDriverConfigPtr cfg =3D virQEMUDriverGetConfig(driver); + char *dumpfile =3D getAutoDumpPath(driver, vm); + + if (!dumpfile) + goto cleanup; + + flags |=3D cfg->autoDumpBypassCache ? VIR_DUMP_BYPASS_CACHE: 0; + if ((ret =3D doCoreDump(driver, vm, dumpfile, flags, + VIR_DOMAIN_CORE_DUMP_FORMAT_RAW)) < 0) + virReportError(VIR_ERR_OPERATION_FAILED, + "%s", _("Dump failed")); + cleanup: + VIR_FREE(dumpfile); + virObjectUnref(cfg); + return ret; +} + +static void +qemuProcessGuestPanicEventInfo(virQEMUDriverPtr driver, + virDomainObjPtr vm, + qemuMonitorEventPanicInfoPtr info) +{ + char *msg =3D qemuMonitorGuestPanicEventInfoFormatMsg(info); + char *timestamp =3D virTimeStringNow(); + + if (msg && timestamp) + qemuDomainLogAppendMessage(driver, vm, "%s: panic %s\n", timestamp= , msg); + + VIR_FREE(timestamp); + VIR_FREE(msg); +} + +void +processGuestPanicEvent(virQEMUDriverPtr driver, + virDomainObjPtr vm, + int action, + qemuMonitorEventPanicInfoPtr info) +{ + qemuDomainObjPrivatePtr priv =3D vm->privateData; + virObjectEventPtr event =3D NULL; + virQEMUDriverConfigPtr cfg =3D virQEMUDriverGetConfig(driver); + bool removeInactive =3D false; + + if (qemuDomainObjBeginAsyncJob(driver, vm, QEMU_ASYNC_JOB_DUMP, + VIR_DOMAIN_JOB_OPERATION_DUMP) < 0) + goto cleanup; + + if (!virDomainObjIsActive(vm)) { + VIR_DEBUG("Ignoring GUEST_PANICKED event from inactive domain %s", + vm->def->name); + goto endjob; + } + + if (info) + qemuProcessGuestPanicEventInfo(driver, vm, info); + + virDomainObjSetState(vm, VIR_DOMAIN_CRASHED, VIR_DOMAIN_CRASHED_PANICK= ED); + + event =3D virDomainEventLifecycleNewFromObj(vm, + VIR_DOMAIN_EVENT_CRASHED, + VIR_DOMAIN_EVENT_CRASHED_PAN= ICKED); + + qemuDomainEventQueue(driver, event); + + if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm, driver->cap= s) < 0) { + VIR_WARN("Unable to save status on vm %s after state change", + vm->def->name); + } + + if (virDomainLockProcessPause(driver->lockManager, vm, &priv->lockStat= e) < 0) + VIR_WARN("Unable to release lease on %s", vm->def->name); + VIR_DEBUG("Preserving lock state '%s'", NULLSTR(priv->lockState)); + + switch (action) { + case VIR_DOMAIN_LIFECYCLE_CRASH_COREDUMP_DESTROY: + if (doCoreDumpToAutoDumpPath(driver, vm, VIR_DUMP_MEMORY_ONLY) < 0) + goto endjob; + ATTRIBUTE_FALLTHROUGH; + + case VIR_DOMAIN_LIFECYCLE_CRASH_DESTROY: + qemuProcessStop(driver, vm, VIR_DOMAIN_SHUTOFF_CRASHED, + QEMU_ASYNC_JOB_DUMP, 0); + event =3D virDomainEventLifecycleNewFromObj(vm, + VIR_DOMAIN_EVENT_STOPPED, + VIR_DOMAIN_EVENT_STOPPED= _CRASHED); + + qemuDomainEventQueue(driver, event); + virDomainAuditStop(vm, "destroyed"); + removeInactive =3D true; + break; + + case VIR_DOMAIN_LIFECYCLE_CRASH_COREDUMP_RESTART: + if (doCoreDumpToAutoDumpPath(driver, vm, VIR_DUMP_MEMORY_ONLY) < 0) + goto endjob; + ATTRIBUTE_FALLTHROUGH; + + case VIR_DOMAIN_LIFECYCLE_CRASH_RESTART: + qemuDomainSetFakeReboot(driver, vm, true); + qemuProcessShutdownOrReboot(driver, vm); + break; + + case VIR_DOMAIN_LIFECYCLE_CRASH_PRESERVE: + break; + + default: + break; + } + + endjob: + qemuDomainObjEndAsyncJob(driver, vm); + if (removeInactive) + qemuDomainRemoveInactiveJob(driver, vm); + + cleanup: + virObjectUnref(cfg); +} + +void +processDeviceDeletedEvent(virQEMUDriverPtr driver, + virDomainObjPtr vm, + char *devAlias) +{ + virQEMUDriverConfigPtr cfg =3D virQEMUDriverGetConfig(driver); + virDomainDeviceDef dev; + + VIR_DEBUG("Removing device %s from domain %p %s", + devAlias, vm, vm->def->name); + + if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0) + goto cleanup; + + if (!virDomainObjIsActive(vm)) { + VIR_DEBUG("Domain is not running"); + goto endjob; + } + + if (STRPREFIX(devAlias, "vcpu")) { + qemuDomainRemoveVcpuAlias(driver, vm, devAlias); + } else { + if (virDomainDefFindDevice(vm->def, devAlias, &dev, true) < 0) + goto endjob; + + if (qemuDomainRemoveDevice(driver, vm, &dev) < 0) + goto endjob; + } + + if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm, driver->cap= s) < 0) + VIR_WARN("unable to save domain status after removing device %s", + devAlias); + + endjob: + qemuDomainObjEndJob(driver, vm); + + cleanup: + VIR_FREE(devAlias); + virObjectUnref(cfg); +} + + + + +void +syncNicRxFilterMacAddr(char *ifname, virNetDevRxFilterPtr guestFilter, + virNetDevRxFilterPtr hostFilter) +{ + char newMacStr[VIR_MAC_STRING_BUFLEN]; + + if (virMacAddrCmp(&hostFilter->mac, &guestFilter->mac)) { + virMacAddrFormat(&guestFilter->mac, newMacStr); + + /* set new MAC address from guest to associated macvtap device */ + if (virNetDevSetMAC(ifname, &guestFilter->mac) < 0) { + VIR_WARN("Couldn't set new MAC address %s to device %s " + "while responding to NIC_RX_FILTER_CHANGED", + newMacStr, ifname); + } else { + VIR_DEBUG("device %s MAC address set to %s", ifname, newMacStr= ); + } + } +} + +static void +syncNicRxFilterGuestMulticast(char *ifname, virNetDevRxFilterPtr guestFilt= er, + virNetDevRxFilterPtr hostFilter) +{ + size_t i, j; + bool found; + char macstr[VIR_MAC_STRING_BUFLEN]; + + for (i =3D 0; i < guestFilter->multicast.nTable; i++) { + found =3D false; + + for (j =3D 0; j < hostFilter->multicast.nTable; j++) { + if (virMacAddrCmp(&guestFilter->multicast.table[i], + &hostFilter->multicast.table[j]) =3D=3D 0) { + found =3D true; + break; + } + } + + if (!found) { + virMacAddrFormat(&guestFilter->multicast.table[i], macstr); + + if (virNetDevAddMulti(ifname, &guestFilter->multicast.table[i]= ) < 0) { + VIR_WARN("Couldn't add new multicast MAC address %s to " + "device %s while responding to NIC_RX_FILTER_CHAN= GED", + macstr, ifname); + } else { + VIR_DEBUG("Added multicast MAC %s to %s interface", + macstr, ifname); + } + } + } +} + +static void +syncNicRxFilterHostMulticast(char *ifname, virNetDevRxFilterPtr guestFilte= r, + virNetDevRxFilterPtr hostFilter) +{ + size_t i, j; + bool found; + char macstr[VIR_MAC_STRING_BUFLEN]; + + for (i =3D 0; i < hostFilter->multicast.nTable; i++) { + found =3D false; + + for (j =3D 0; j < guestFilter->multicast.nTable; j++) { + if (virMacAddrCmp(&hostFilter->multicast.table[i], + &guestFilter->multicast.table[j]) =3D=3D 0) { + found =3D true; + break; + } + } + + if (!found) { + virMacAddrFormat(&hostFilter->multicast.table[i], macstr); + + if (virNetDevDelMulti(ifname, &hostFilter->multicast.table[i])= < 0) { + VIR_WARN("Couldn't delete multicast MAC address %s from " + "device %s while responding to NIC_RX_FILTER_CHAN= GED", + macstr, ifname); + } else { + VIR_DEBUG("Deleted multicast MAC %s from %s interface", + macstr, ifname); + } + } + } +} + + +static void +syncNicRxFilterPromiscMode(char *ifname, + virNetDevRxFilterPtr guestFilter, + virNetDevRxFilterPtr hostFilter) +{ + bool promisc; + bool setpromisc =3D false; + + /* Set macvtap promisc mode to true if the guest has vlans defined */ + /* or synchronize the macvtap promisc mode if different from guest */ + if (guestFilter->vlan.nTable > 0) { + if (!hostFilter->promiscuous) { + setpromisc =3D true; + promisc =3D true; + } + } else if (hostFilter->promiscuous !=3D guestFilter->promiscuous) { + setpromisc =3D true; + promisc =3D guestFilter->promiscuous; + } + + if (setpromisc) { + if (virNetDevSetPromiscuous(ifname, promisc) < 0) { + VIR_WARN("Couldn't set PROMISC flag to %s for device %s " + "while responding to NIC_RX_FILTER_CHANGED", + promisc ? "true" : "false", ifname); + } + } +} + +static void +syncNicRxFilterMultiMode(char *ifname, virNetDevRxFilterPtr guestFilter, + virNetDevRxFilterPtr hostFilter) +{ + if (hostFilter->multicast.mode !=3D guestFilter->multicast.mode) { + switch (guestFilter->multicast.mode) { + case VIR_NETDEV_RX_FILTER_MODE_ALL: + if (virNetDevSetRcvAllMulti(ifname, true)) { + + VIR_WARN("Couldn't set allmulticast flag to 'on' for " + "device %s while responding to " + "NIC_RX_FILTER_CHANGED", ifname); + } + break; + + case VIR_NETDEV_RX_FILTER_MODE_NORMAL: + if (virNetDevSetRcvMulti(ifname, true)) { + + VIR_WARN("Couldn't set multicast flag to 'on' for " + "device %s while responding to " + "NIC_RX_FILTER_CHANGED", ifname); + } + + if (virNetDevSetRcvAllMulti(ifname, false)) { + VIR_WARN("Couldn't set allmulticast flag to 'off' for " + "device %s while responding to " + "NIC_RX_FILTER_CHANGED", ifname); + } + break; + + case VIR_NETDEV_RX_FILTER_MODE_NONE: + if (virNetDevSetRcvAllMulti(ifname, false)) { + VIR_WARN("Couldn't set allmulticast flag to 'off' for " + "device %s while responding to " + "NIC_RX_FILTER_CHANGED", ifname); + } + + if (virNetDevSetRcvMulti(ifname, false)) { + VIR_WARN("Couldn't set multicast flag to 'off' for " + "device %s while responding to " + "NIC_RX_FILTER_CHANGED", + ifname); + } + break; + } + } +} + +void +syncNicRxFilterDeviceOptions(char *ifname, virNetDevRxFilterPtr guestFilte= r, + virNetDevRxFilterPtr hostFilter) +{ + syncNicRxFilterPromiscMode(ifname, guestFilter, hostFilter); + syncNicRxFilterMultiMode(ifname, guestFilter, hostFilter); +} + + +void +syncNicRxFilterMulticast(char *ifname, + virNetDevRxFilterPtr guestFilter, + virNetDevRxFilterPtr hostFilter) +{ + syncNicRxFilterGuestMulticast(ifname, guestFilter, hostFilter); + syncNicRxFilterHostMulticast(ifname, guestFilter, hostFilter); +} + +void +processNicRxFilterChangedEvent(virQEMUDriverPtr driver, + virDomainObjPtr vm, + char *devAlias) +{ + virQEMUDriverConfigPtr cfg =3D virQEMUDriverGetConfig(driver); + qemuDomainObjPrivatePtr priv =3D vm->privateData; + virDomainDeviceDef dev; + virDomainNetDefPtr def; + virNetDevRxFilterPtr guestFilter =3D NULL; + virNetDevRxFilterPtr hostFilter =3D NULL; + int ret; + + VIR_DEBUG("Received NIC_RX_FILTER_CHANGED event for device %s " + "from domain %p %s", + devAlias, vm, vm->def->name); + + if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0) + goto cleanup; + + if (!virDomainObjIsActive(vm)) { + VIR_DEBUG("Domain is not running"); + goto endjob; + } + + if (virDomainDefFindDevice(vm->def, devAlias, &dev, true) < 0) { + VIR_WARN("NIC_RX_FILTER_CHANGED event received for " + "non-existent device %s in domain %s", + devAlias, vm->def->name); + goto endjob; + } + if (dev.type !=3D VIR_DOMAIN_DEVICE_NET) { + VIR_WARN("NIC_RX_FILTER_CHANGED event received for " + "non-network device %s in domain %s", + devAlias, vm->def->name); + goto endjob; + } + def =3D dev.data.net; + + if (!virDomainNetGetActualTrustGuestRxFilters(def)) { + VIR_DEBUG("ignore NIC_RX_FILTER_CHANGED event for network " + "device %s in domain %s", + def->info.alias, vm->def->name); + /* not sending "query-rx-filter" will also suppress any + * further NIC_RX_FILTER_CHANGED events for this device + */ + goto endjob; + } + + /* handle the event - send query-rx-filter and respond to it. */ + + VIR_DEBUG("process NIC_RX_FILTER_CHANGED event for network " + "device %s in domain %s", def->info.alias, vm->def->name); + + qemuDomainObjEnterMonitor(driver, vm); + ret =3D qemuMonitorQueryRxFilter(priv->mon, devAlias, &guestFilter); + if (qemuDomainObjExitMonitor(driver, vm) < 0) + ret =3D -1; + if (ret < 0) + goto endjob; + + if (virDomainNetGetActualType(def) =3D=3D VIR_DOMAIN_NET_TYPE_DIRECT) { + + if (virNetDevGetRxFilter(def->ifname, &hostFilter)) { + VIR_WARN("Couldn't get current RX filter for device %s " + "while responding to NIC_RX_FILTER_CHANGED", + def->ifname); + goto endjob; + } + + /* For macvtap connections, set the following macvtap network devi= ce + * attributes to match those of the guest network device: + * - MAC address + * - Multicast MAC address table + * - Device options: + * - PROMISC + * - MULTICAST + * - ALLMULTI + */ + syncNicRxFilterMacAddr(def->ifname, guestFilter, hostFilter); + syncNicRxFilterMulticast(def->ifname, guestFilter, hostFilter); + syncNicRxFilterDeviceOptions(def->ifname, guestFilter, hostFilter); + } + + if (virDomainNetGetActualType(def) =3D=3D VIR_DOMAIN_NET_TYPE_NETWORK)= { + const char *brname =3D virDomainNetGetActualBridgeName(def); + + /* For libivrt network connections, set the following TUN/TAP netw= ork + * device attributes to match those of the guest network device: + * - QoS filters (which are based on MAC address) + */ + if (virDomainNetGetActualBandwidth(def) && + def->data.network.actual && + virNetDevBandwidthUpdateFilter(brname, &guestFilter->mac, + def->data.network.actual->class= _id) < 0) + goto endjob; + } + + endjob: + qemuDomainObjEndJob(driver, vm); + + cleanup: + virNetDevRxFilterFree(hostFilter); + virNetDevRxFilterFree(guestFilter); + VIR_FREE(devAlias); + virObjectUnref(cfg); +} + +void +processSerialChangedEvent(virQEMUDriverPtr driver, + virDomainObjPtr vm, + char *devAlias, + bool connected) +{ + virQEMUDriverConfigPtr cfg =3D virQEMUDriverGetConfig(driver); + virDomainChrDeviceState newstate; + virObjectEventPtr event =3D NULL; + virDomainDeviceDef dev; + qemuDomainObjPrivatePtr priv =3D vm->privateData; + + if (connected) + newstate =3D VIR_DOMAIN_CHR_DEVICE_STATE_CONNECTED; + else + newstate =3D VIR_DOMAIN_CHR_DEVICE_STATE_DISCONNECTED; + + VIR_DEBUG("Changing serial port state %s in domain %p %s", + devAlias, vm, vm->def->name); + + if (newstate =3D=3D VIR_DOMAIN_CHR_DEVICE_STATE_DISCONNECTED && + virDomainObjIsActive(vm) && priv->agent) { + /* peek into the domain definition to find the channel */ + if (virDomainDefFindDevice(vm->def, devAlias, &dev, true) =3D=3D 0= && + dev.type =3D=3D VIR_DOMAIN_DEVICE_CHR && + dev.data.chr->deviceType =3D=3D VIR_DOMAIN_CHR_DEVICE_TYPE_CHA= NNEL && + dev.data.chr->targetType =3D=3D VIR_DOMAIN_CHR_CHANNEL_TARGET_= TYPE_VIRTIO && + STREQ_NULLABLE(dev.data.chr->target.name, "org.qemu.guest_agen= t.0")) + /* Close agent monitor early, so that other threads + * waiting for the agent to reply can finish and our + * job we acquire below can succeed. */ + qemuAgentNotifyClose(priv->agent); + + /* now discard the data, since it may possibly change once we unlo= ck + * while entering the job */ + memset(&dev, 0, sizeof(dev)); + } + + if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0) + goto cleanup; + + if (!virDomainObjIsActive(vm)) { + VIR_DEBUG("Domain is not running"); + goto endjob; + } + + if (virDomainDefFindDevice(vm->def, devAlias, &dev, true) < 0) + goto endjob; + + /* we care only about certain devices */ + if (dev.type !=3D VIR_DOMAIN_DEVICE_CHR || + dev.data.chr->deviceType !=3D VIR_DOMAIN_CHR_DEVICE_TYPE_CHANNEL || + dev.data.chr->targetType !=3D VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_V= IRTIO) + goto endjob; + + dev.data.chr->state =3D newstate; + + if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm, driver->cap= s) < 0) + VIR_WARN("unable to save status of domain %s after updating state = of " + "channel %s", vm->def->name, devAlias); + + if (STREQ_NULLABLE(dev.data.chr->target.name, "org.qemu.guest_agent.0"= )) { + if (newstate =3D=3D VIR_DOMAIN_CHR_DEVICE_STATE_CONNECTED) { + if (qemuConnectAgent(driver, vm) < 0) + goto endjob; + } else { + if (priv->agent) { + qemuAgentClose(priv->agent); + priv->agent =3D NULL; + } + priv->agentError =3D false; + } + + event =3D virDomainEventAgentLifecycleNewFromObj(vm, newstate, + VIR_CONNECT_DOMAIN_= EVENT_AGENT_LIFECYCLE_REASON_CHANNEL); + qemuDomainEventQueue(driver, event); + } + + endjob: + qemuDomainObjEndJob(driver, vm); + + cleanup: + VIR_FREE(devAlias); + virObjectUnref(cfg); + +} + +void +processBlockJobEvent(virQEMUDriverPtr driver, + virDomainObjPtr vm, + char *diskAlias, + int type, + int status) +{ + virDomainDiskDefPtr disk; + + if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0) + goto cleanup; + + if (!virDomainObjIsActive(vm)) { + VIR_DEBUG("Domain is not running"); + goto endjob; + } + + if ((disk =3D qemuProcessFindDomainDiskByAlias(vm, diskAlias))) + qemuBlockJobEventProcess(driver, vm, disk, QEMU_ASYNC_JOB_NONE, ty= pe, status); + + endjob: + qemuDomainObjEndJob(driver, vm); + cleanup: + VIR_FREE(diskAlias); +} + +void +processMonitorEOFEvent(virQEMUDriverPtr driver, + virDomainObjPtr vm) +{ + qemuDomainObjPrivatePtr priv =3D vm->privateData; + int eventReason =3D VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN; + int stopReason =3D VIR_DOMAIN_SHUTOFF_SHUTDOWN; + const char *auditReason =3D "shutdown"; + unsigned int stopFlags =3D 0; + virObjectEventPtr event =3D NULL; + + if (qemuProcessBeginStopJob(driver, vm, QEMU_JOB_DESTROY, true) < 0) + return; + + if (!virDomainObjIsActive(vm)) { + VIR_DEBUG("Domain %p '%s' is not active, ignoring EOF", + vm, vm->def->name); + goto endjob; + } + + if (priv->monJSON && !priv->gotShutdown) { + VIR_DEBUG("Monitor connection to '%s' closed without SHUTDOWN even= t; " + "assuming the domain crashed", vm->def->name); + eventReason =3D VIR_DOMAIN_EVENT_STOPPED_FAILED; + stopReason =3D VIR_DOMAIN_SHUTOFF_CRASHED; + auditReason =3D "failed"; + } + + if (priv->job.asyncJob =3D=3D QEMU_ASYNC_JOB_MIGRATION_IN) { + stopFlags |=3D VIR_QEMU_PROCESS_STOP_MIGRATED; + qemuMigrationErrorSave(driver, vm->def->name, + qemuMonitorLastError(priv->mon)); + } + + event =3D virDomainEventLifecycleNewFromObj(vm, VIR_DOMAIN_EVENT_STOPP= ED, + eventReason); + qemuProcessStop(driver, vm, stopReason, QEMU_ASYNC_JOB_NONE, stopFlags= ); + virDomainAuditStop(vm, auditReason); + qemuDomainEventQueue(driver, event); + + endjob: + qemuDomainRemoveInactive(driver, vm); + qemuDomainObjEndJob(driver, vm); +} diff --git a/src/qemu/qemu_process.h b/src/qemu/qemu_process.h index a2bbc4f..95007b5 100644 --- a/src/qemu/qemu_process.h +++ b/src/qemu/qemu_process.h @@ -25,6 +25,92 @@ # include "qemu_conf.h" # include "qemu_domain.h" =20 +typedef enum { + QEMU_SAVE_FORMAT_RAW =3D 0, + QEMU_SAVE_FORMAT_GZIP =3D 1, + QEMU_SAVE_FORMAT_BZIP2 =3D 2, + /* + * Deprecated by xz and never used as part of a release + * QEMU_SAVE_FORMAT_LZMA + */ + QEMU_SAVE_FORMAT_XZ =3D 3, + QEMU_SAVE_FORMAT_LZOP =3D 4, + /* Note: add new members only at the end. + These values are used in the on-disk format. + Do not change or re-use numbers. */ + + QEMU_SAVE_FORMAT_LAST +} virQEMUSaveFormat; + +VIR_ENUM_DECL(qemuSaveCompression) +VIR_ENUM_DECL(qemuDumpFormat) + +int +qemuFileWrapperFDClose(virDomainObjPtr vm, + virFileWrapperFdPtr fd); + +int +qemuGetCompressionProgram(const char *imageFormat, + char **compresspath, + const char *styleFormat, + bool use_raw_on_fail); +int +qemuOpenFile(virQEMUDriverPtr driver, + virDomainObjPtr vm, + const char *path, + int oflags, + bool *needUnlink, + bool *bypassSecurityDriver); +int +doCoreDump(virQEMUDriverPtr driver, + virDomainObjPtr vm, + const char *path, + unsigned int dump_flags, + unsigned int dumpformat); + +void +syncNicRxFilterMacAddr(char *ifname, virNetDevRxFilterPtr guestFilter, + virNetDevRxFilterPtr hostFilter); + +void +syncNicRxFilterDeviceOptions(char *ifname, virNetDevRxFilterPtr guestFilte= r, + virNetDevRxFilterPtr hostFilter); + +void +syncNicRxFilterMulticast(char *ifname, + virNetDevRxFilterPtr guestFilter, + virNetDevRxFilterPtr hostFilter); + +void +processWatchdogEvent(virQEMUDriverPtr driver, + virDomainObjPtr vm, + int action); +void +processGuestPanicEvent(virQEMUDriverPtr driver, + virDomainObjPtr vm, + int action, + qemuMonitorEventPanicInfoPtr info); +void +processNicRxFilterChangedEvent(virQEMUDriverPtr driver, + virDomainObjPtr vm, + char *devAlias); +void +processSerialChangedEvent(virQEMUDriverPtr driver, + virDomainObjPtr vm, + char *devAlias, + bool connected); + +void +processBlockJobEvent(virQEMUDriverPtr driver, + virDomainObjPtr vm, + char *diskAlias, + int type, + int status); +void processMonitorEOFEvent(virQEMUDriverPtr driver, + virDomainObjPtr vm); +void processDeviceDeletedEvent(virQEMUDriverPtr driver, + virDomainObjPtr vm, + char *devAlias); int qemuProcessPrepareMonitorChr(virDomainChrSourceDefPtr monConfig, const char *domainDir); =20 --=20 2.9.5 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list