From nobody Fri Mar 29 08:16:24 2024 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; arc=pass (i=1 dmarc=pass fromdomain=virtuozzo.com); dmarc=pass(p=quarantine dis=none) header.from=virtuozzo.com ARC-Seal: i=2; a=rsa-sha256; t=1663857237; cv=pass; d=zohomail.com; s=zohoarc; b=FJw9oX6xYiUebVKNDSkQrSEHEhoUB8T7t8nXool4nkzRahDQHQnNiuZmsIzVg5RzZd3X8tHVq4y0zHg7fxxPKaOlYzOHU8Phq16/pDgb5zggugsjhUwBPtIOK45d1eHnNLfhP9KTldfazBAFOx1eSfRUGaZr26Fyjn0D5YbMaLU= ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1663857237; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=ihseUROg9ZqBMMrrLOa6+A3f4Xw78nf99JdxsBQn0Wg=; b=LglEq014+yzNMcoIKQK8gg7gQt6h2647CnBhmkvvUhtY9qg0zXNJDfUjVxeQ6j8UjVGE0ZgLjlJ6RF0bwdIPdZFOtC2YoZBZSd1eFv58+uul79SjN2Itcvijr1dXX9O6OqPq64JyqxQIX7YkdI38RbBhInZzyHo7TuahKB1L2dk= ARC-Authentication-Results: i=2; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; arc=pass (i=1 dmarc=pass fromdomain=virtuozzo.com); dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1663857237464295.2785002663784; Thu, 22 Sep 2022 07:33:57 -0700 (PDT) Received: from localhost ([::1]:59702 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1obNH2-0000S5-Dz for importer@patchew.org; Thu, 22 Sep 2022 10:33:56 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:40666) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1obM8f-0001YA-KT for qemu-devel@nongnu.org; Thu, 22 Sep 2022 09:21:26 -0400 Received: from mail-eopbgr60136.outbound.protection.outlook.com ([40.107.6.136]:7326 helo=EUR04-DB3-obe.outbound.protection.outlook.com) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1obM8W-0004pt-6R for qemu-devel@nongnu.org; Thu, 22 Sep 2022 09:21:08 -0400 Received: from AS8PR08MB7095.eurprd08.prod.outlook.com (2603:10a6:20b:402::11) by PAXPR08MB6480.eurprd08.prod.outlook.com (2603:10a6:102:155::10) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.5654.16; Thu, 22 Sep 2022 13:20:12 +0000 Received: from AS8PR08MB7095.eurprd08.prod.outlook.com ([fe80::5174:25c7:6df8:741b]) by AS8PR08MB7095.eurprd08.prod.outlook.com ([fe80::5174:25c7:6df8:741b%8]) with mapi id 15.20.5654.016; Thu, 22 Sep 2022 13:20:12 +0000 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=ARNP1AMvp4Q8NCYzjjMGyRYAiorqM9NTnqto6KKPcx7YE7VqjvwEBr9z8Yakbsd4ja1G4r1EFuuMB2heAIqc0oTMPXurWdU+PIPB6zCJPeF8WBSiXhbwzTKj2tlwe1VKi38+Zo65I+7IU/6uTUkj8NcDugZA+BvB7n6O3XwBp6+wFQTNpP9KlVm1miVHC24h7OUz8Rz/6bIEg6R2pihgRX9nwtry+JQafzKjEftxLQjF7wOvEeaszJDLxoS/oGkQS3ImY4ZyWjbUQve+oo6TdNIQ0rQfTYMhG9q6n2yCAkmY/lw9408AKvt86VxnAa3LcKt8Fz/NHHiODcnK53HEvg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=ihseUROg9ZqBMMrrLOa6+A3f4Xw78nf99JdxsBQn0Wg=; b=IvKohOF4T+ugr6gVx3FNFLX45AyJSys3PeaSKliejMKR0AmfZOj6ws6D1PZ/LmaYp483xZjFS0e9sS3QlgPpxou5yh/gSUnPnspCF8HI/lJcbUUI73az2l/8E6I5yJcNsd0Z8iPmAWP8XyxmStEA1O+IlbaXwO0XxBWO/LNE5nj99IuPPYgVj+VrZ4vm3reIRW0wTTvekAr0GRvzoO+4nZ/d3eN6zpmFucqEz9fHut2tY95r4Ae6V2eKENDBEnkx6JuY/fNVpGQoB9qfMQ1xGstqqlpph6kupZ9SpOwmoHqCVCUS03ofAkAMdlyT8UtQFQCDvA4TKMeuuErrGMJd8Q== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=virtuozzo.com; dmarc=pass action=none header.from=virtuozzo.com; dkim=pass header.d=virtuozzo.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=virtuozzo.com; s=selector2; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=ihseUROg9ZqBMMrrLOa6+A3f4Xw78nf99JdxsBQn0Wg=; b=cZCEnji3HNUIWUwOjXXqnTSDUGHBomb3E6oxXN+X2bc3nKmaPZaC71la68QooniPkZT9gvpAc+E4OXNWKixa5YUerKncY1FqnHFFjw8Cj0zEyP1SgZP8FQKMkpq0dNdEoMEjO9DRot6Ia0kaQfkh7k/W7dOUZnvElj7n3YDE6MupAceZcLZ2hkrba9sH4pkCA7CzMCeMMqTmOJAFbF2NNWLECCbipnJFIvyihmc2hN/wsejqKoftCRwhy3zusQUnLa2lqN8nl6+UiJEL8Iu1Er/JOhg7iewRkG+qLLjsg+JfZD8oRtlob3FiKNdANH/hJjjuFwwy/QmqsO6gFu5/tg== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=virtuozzo.com; From: Alexander Ivanov To: qemu-devel@nongnu.org Cc: den@virtuozzo.com, michael.roth@amd.com, kkostiuk@redhat.com Subject: [PATCH 1/5] qga: Move Linux-specific commands code to separate file Date: Thu, 22 Sep 2022 15:19:57 +0200 Message-Id: <20220922132001.940334-2-alexander.ivanov@virtuozzo.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220922132001.940334-1-alexander.ivanov@virtuozzo.com> References: <20220922132001.940334-1-alexander.ivanov@virtuozzo.com> Content-Transfer-Encoding: quoted-printable X-ClientProxiedBy: FR0P281CA0083.DEUP281.PROD.OUTLOOK.COM (2603:10a6:d10:1e::18) To AS8PR08MB7095.eurprd08.prod.outlook.com (2603:10a6:20b:402::11) MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: AS8PR08MB7095:EE_|PAXPR08MB6480:EE_ X-MS-Office365-Filtering-Correlation-Id: 53704be6-a1b0-4874-77d8-08da9c9d2dc6 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: 1BmV6Hsn6fNzaiW4opx2c5QG2Bk3+zk2OMeb3iPG0AayyirN2VajPd6wiaudwFxe55PkGrWbI9XMro5/K7rzG21iu0IEhwNH+KSlj/mIb5AFlnLnR1JHjfzAVelX94gV7Qfyzh8J29GSnHVsfdZSZFKpPWJlPwt+a4qNKyQs3RL865P3hEUnD6hdt2j10DZHjYWf9VvDTn8A3Vi4vzgCq4xuDHbS9kQbbr8scvtU71l+DzhqTI/xuXcEzxFFzav79fVsjRX6nl3kRM9w3s96CLUg8RECIhMiMu6Mb8R+EzLhYea0NWjpcjw01ppjvkY+2bErgrp3WPPcxK+O/ttdT7jVr4WZ2BV6TtAUpwvMjDUmg8y6uvKGSu48wfi1u9Y3WWkUKjs3tG1pfxSlqGeKbBnX3h1/fWMkLSnp2UUTav4MtE6BqJ8WDpAjHbGa17UlKhhNjBu63cWKgFmKrEvq48aecDizWV/B/mgPlmetFsNxhqSVwy4dT3E3ZSfyskOu7mKBw2cfGI+og4/x6n7rarblTRzyBIiJ5eIo6ttmy5Bg3/W50Odtk10cc/aqwUzl2u2wE1MXouu/zdhD+m1YZR3TbsXo9wh/8Xr/IMSW7twVsWq+oPmtULq7PJh2/RbUdsdwnxzx0LJawqEQPJ7VKf0MuKQVCez643Orqk+fFTA4o5AmQ50hCTFkgoVY869057kHvMfOLltq3o8Wznpal5g/CACsUHcplxPYnooFcS+kkhLZ+s31GdWCLJYR77FrWZVijw3wfZHUtBEjAmzwb5mu0iwuDm7FvZ2uDBl11KM= X-Forefront-Antispam-Report: CIP:255.255.255.255; CTRY:; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:AS8PR08MB7095.eurprd08.prod.outlook.com; PTR:; CAT:NONE; SFS:(13230022)(4636009)(346002)(366004)(376002)(136003)(396003)(39850400004)(451199015)(41300700001)(6486002)(478600001)(6666004)(52116002)(36756003)(66476007)(66556008)(316002)(186003)(66946007)(83380400001)(1076003)(5660300002)(2616005)(8936002)(26005)(6506007)(6916009)(86362001)(8676002)(44832011)(4326008)(2906002)(6512007)(30864003)(38350700002)(38100700002)(21314003)(579004)(559001); DIR:OUT; SFP:1102; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?us-ascii?Q?lyAW6eF5HxTPIvgiBLY/aJVGhCpKn40Pbx78BL7AWAihsS+4KiohPfpeUNUl?= =?us-ascii?Q?OLBpNQ+HBA2nsBqHHNsN2SY+xBTOPdROLK3i8btqGcpHPpop5ZtvvvRMH8aN?= =?us-ascii?Q?mS5v7ExELH39wrGXk03fCG+aGYMzELJVQXNLmBNlLFqLQ7TtDyElWPpbqXzx?= =?us-ascii?Q?X//kmZl7XqN+IoKFJ0jNaDBauTxseKg47ZfTnQUxLyRu02FfxQXK0x54KdbI?= =?us-ascii?Q?haegkqZluD1V4UgNFZyvJ2Q1g6iEnT1yOJKpxlCqWdMr3dxckwXwtr6awnkh?= =?us-ascii?Q?pTolWYPCBmcXLUCMaIWWx41UDFD0Gj4AofmbxvJfxVdP3MJm9GhirP669YzH?= =?us-ascii?Q?Qv58iKwGx+JC+dNeyqXg0NMgwbXahsHA9ZJ64uAFf1mYqbDRe3JCmmqkyv4t?= =?us-ascii?Q?a7U0j8de0agVoK18crTXSfXiswgplBAiciOb2WudZXSyHNHuEq5BABbu6iH5?= =?us-ascii?Q?YWIPBX6Ye89rvU3Ew+l6YleAOmLzfdnsnPcEljwlHBKHIxqyOCxFoW3eR4RK?= =?us-ascii?Q?/AAdjcawNww8ozxQG6qZMQKOxIUy+6rVLBs33a4KzzL+w/xVpKfsNABufnzS?= =?us-ascii?Q?KWnmNefp5/FDEc90YSDqpV2GWNX5DpoPxpqoI5xOypAIWpqNs4As8pV77fLl?= =?us-ascii?Q?LQYJJ0pP2dYY3Fg3gc7TJ2Q7w+UAcdlv8pzfRwffyTdyp4VMg+qZdt1fUdTE?= =?us-ascii?Q?B01izC2sSJbvDX/9MLEggZ9X2ZqRtnvL2RYtpp5vuQtFg8bcX81MJKx5eaqc?= =?us-ascii?Q?LenOcfpL9o9/nBlCBnwnG/oYv7M+QHgCLsY51qITyzztp3Kbsb9RdFO/bD5p?= =?us-ascii?Q?iWwasuGIfYkypSDbE+95HmX9OtenGNkISejdcCcbrkXcCqlKnkUVkEl//Wnu?= =?us-ascii?Q?lHkRkymvT12q0KsjMnP1Hq+jfrhf4WkTTeryTXM5u/tzsMIkz9b5sueYwXNG?= =?us-ascii?Q?tleYtwaobtnvZPMFRaAZSDGpZbsleYCYMfxTHhx31jczwkJjr8UisxzzPupV?= =?us-ascii?Q?9y+pr0qx1yXLJBNhKUEojE2/FZhSl1pd0GxKjuLTDibdFArg0g0kr44WMR2g?= =?us-ascii?Q?K/D9Jx1chIC+DWdvGA5+hZfTq8R7qhE2igVOMreV2IT8qb7apCZeFdntNT78?= =?us-ascii?Q?aPTbVypq1TtTOjiP6qbvA0/9goKPZ3Sk+L+ID65g2KGWCAXrMxmzh8xkerhj?= =?us-ascii?Q?ENjKlru3+idyy6x5WLjEBkux42ZV2MLTWXNrIyphu+SzskX8kWgN8x9d5zFq?= =?us-ascii?Q?SgQElWiB5xC4F4Pk45hll/Nqphvj2+I4RHBmWN4HCCeBrWNxuhuswmlB3wmu?= =?us-ascii?Q?eUBrmaggB1e1YlAN2tYlv7vR8XO3MeRhur57UyIRFBjTyMTDmbXOOXnwBiq/?= =?us-ascii?Q?QiwxbdZQ3NGVaj3nXBpeAuLm/KOFYFlcv7MAtAiMLRAEEmCfINVvdOP/b4RD?= =?us-ascii?Q?TUU4hVDOwovgq9H5apY1KUdIH0223vwugtQssbSkfgXv77BpRBBg5us9S6wh?= =?us-ascii?Q?/K2EhWVWYXOphDO6wa1Xa/E8IKeaV4VEBEctbjnkjqKgzEwVykYkqhIzGT/M?= =?us-ascii?Q?hVAcxJsFHJJ80vT/fJVezDDywyBaeSag5ktHHZPn1Hv53qNwdU10l8gDL3wp?= =?us-ascii?Q?ctuACCrzSpVNd0O1A0KMKis=3D?= X-OriginatorOrg: virtuozzo.com X-MS-Exchange-CrossTenant-Network-Message-Id: 53704be6-a1b0-4874-77d8-08da9c9d2dc6 X-MS-Exchange-CrossTenant-AuthSource: AS8PR08MB7095.eurprd08.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 22 Sep 2022 13:20:12.2500 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 0bc7f26d-0264-416e-a6fc-8352af79c58f X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: Pdpm5D6HNY+Ac4CvKy/aibYZX/wZaXZvBPHNZgJjmvTMLSAsLZRfI6utdmtnO2KPwE1NfoJUUPxwdzQyoJwGrBptcZSyaBZ0rCCPb9WujPw= X-MS-Exchange-Transport-CrossTenantHeadersStamped: PAXPR08MB6480 Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=40.107.6.136; envelope-from=alexander.ivanov@virtuozzo.com; helo=EUR04-DB3-obe.outbound.protection.outlook.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H2=-0.001, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail-DKIM: pass (identity @virtuozzo.com) X-ZM-MESSAGEID: 1663857241610100001 Content-Type: text/plain; charset="utf-8" In the next patches we are going to add FreeBSD support for QEMU Guest Agent. In the result, code in commands-posix.c will be too cumbersome. Move Linux-specific code to a separate file keeping common POSIX code in commands-posix.c. Signed-off-by: Alexander Ivanov --- qga/commands-common.h | 34 + qga/commands-linux.c | 2242 ++++++++++++++++++++++++++++++++++++ qga/commands-posix.c | 2558 +++-------------------------------------- qga/meson.build | 3 + 4 files changed, 2447 insertions(+), 2390 deletions(-) create mode 100644 qga/commands-linux.c diff --git a/qga/commands-common.h b/qga/commands-common.h index d0e4a9696f..aa0472ea4c 100644 --- a/qga/commands-common.h +++ b/qga/commands-common.h @@ -29,4 +29,38 @@ GuestFileRead *guest_file_read_unsafe(GuestFileHandle *g= fh, */ char *qga_get_host_name(Error **errp); =20 +void ga_wait_child(pid_t pid, int *status, Error **errp); + +#if defined(__linux__) +#include +#ifdef FIFREEZE +#define CONFIG_FSFREEZE #endif +#ifdef FITRIM +#define CONFIG_FSTRIM +#endif +#endif /* __linux__*/ + +#if defined(CONFIG_FSFREEZE) || defined(CONFIG_FSTRIM) +typedef struct FsMount { + char *dirname; + char *devtype; + unsigned int devmajor, devminor; + QTAILQ_ENTRY(FsMount) next; +} FsMount; + +typedef QTAILQ_HEAD(FsMountList, FsMount) FsMountList; + +bool build_fs_mount_list(FsMountList *mounts, Error **errp); +void free_fs_mount_list(FsMountList *mounts); +#endif + +#if defined(CONFIG_FSFREEZE) +int64_t qmp_guest_fsfreeze_do_freeze_list(bool has_mountpoints, + strList *mountpoints, + FsMountList mounts, + Error **errp); +int qmp_guest_fsfreeze_do_thaw(Error **errp); +#endif + +#endif /* QGA_COMMANDS_COMMON_H */ diff --git a/qga/commands-linux.c b/qga/commands-linux.c new file mode 100644 index 0000000000..615e9a0027 --- /dev/null +++ b/qga/commands-linux.c @@ -0,0 +1,2242 @@ +/* + * QEMU Guest Agent Linux-specific command implementations + * + * Copyright IBM Corp. 2011 + * + * Authors: + * Michael Roth + * Michal Privoznik + * + * This work is licensed under the terms of the GNU GPL, version 2 or late= r. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include +#include "guest-agent-core.h" +#include "qga-qapi-commands.h" +#include "qapi/qmp/qerror.h" +#include "qapi/error.h" +#include "qemu/queue.h" +#include "qemu/base64.h" +#include "commands-common.h" +#include "block/nvme.h" +#include "cutils.h" + +#include +#include +#include + +#ifdef CONFIG_LIBUDEV +#include +#endif + +#if defined(CONFIG_FSFREEZE) || defined(CONFIG_FSTRIM) +static int dev_major_minor(const char *devpath, + unsigned int *devmajor, unsigned int *devminor) +{ + struct stat st; + + *devmajor =3D 0; + *devminor =3D 0; + + if (stat(devpath, &st) < 0) { + slog("failed to stat device file '%s': %s", devpath, strerror(errn= o)); + return -1; + } + if (S_ISDIR(st.st_mode)) { + /* It is bind mount */ + return -2; + } + if (S_ISBLK(st.st_mode)) { + *devmajor =3D major(st.st_rdev); + *devminor =3D minor(st.st_rdev); + return 0; + } + return -1; +} + +/* + * Walk the mount table and build a list of local file systems + */ +static bool build_fs_mount_list_from_mtab(FsMountList *mounts, Error **err= p) +{ + struct mntent *ment; + FsMount *mount; + char const *mtab =3D "/proc/self/mounts"; + FILE *fp; + unsigned int devmajor, devminor; + + fp =3D setmntent(mtab, "r"); + if (!fp) { + error_setg(errp, "failed to open mtab file: '%s'", mtab); + return false; + } + + while ((ment =3D getmntent(fp))) { + /* + * An entry which device name doesn't start with a '/' is + * either a dummy file system or a network file system. + * Add special handling for smbfs and cifs as is done by + * coreutils as well. + */ + if ((ment->mnt_fsname[0] !=3D '/') || + (strcmp(ment->mnt_type, "smbfs") =3D=3D 0) || + (strcmp(ment->mnt_type, "cifs") =3D=3D 0)) { + continue; + } + if (dev_major_minor(ment->mnt_fsname, &devmajor, &devminor) =3D=3D= -2) { + /* Skip bind mounts */ + continue; + } + + mount =3D g_new0(FsMount, 1); + mount->dirname =3D g_strdup(ment->mnt_dir); + mount->devtype =3D g_strdup(ment->mnt_type); + mount->devmajor =3D devmajor; + mount->devminor =3D devminor; + + QTAILQ_INSERT_TAIL(mounts, mount, next); + } + + endmntent(fp); + return true; +} + +static void decode_mntname(char *name, int len) +{ + int i, j =3D 0; + for (i =3D 0; i <=3D len; i++) { + if (name[i] !=3D '\\') { + name[j++] =3D name[i]; + } else if (name[i + 1] =3D=3D '\\') { + name[j++] =3D '\\'; + i++; + } else if (name[i + 1] >=3D '0' && name[i + 1] <=3D '3' && + name[i + 2] >=3D '0' && name[i + 2] <=3D '7' && + name[i + 3] >=3D '0' && name[i + 3] <=3D '7') { + name[j++] =3D (name[i + 1] - '0') * 64 + + (name[i + 2] - '0') * 8 + + (name[i + 3] - '0'); + i +=3D 3; + } else { + name[j++] =3D name[i]; + } + } +} + +bool build_fs_mount_list(FsMountList *mounts, Error **errp) +{ + FsMount *mount; + char const *mountinfo =3D "/proc/self/mountinfo"; + FILE *fp; + char *line =3D NULL, *dash; + size_t n; + char check; + unsigned int devmajor, devminor; + int ret, dir_s, dir_e, type_s, type_e, dev_s, dev_e; + + fp =3D fopen(mountinfo, "r"); + if (!fp) { + return build_fs_mount_list_from_mtab(mounts, errp); + } + + while (getline(&line, &n, fp) !=3D -1) { + ret =3D sscanf(line, "%*u %*u %u:%u %*s %n%*s%n%c", + &devmajor, &devminor, &dir_s, &dir_e, &check); + if (ret < 3) { + continue; + } + dash =3D strstr(line + dir_e, " - "); + if (!dash) { + continue; + } + ret =3D sscanf(dash, " - %n%*s%n %n%*s%n%c", + &type_s, &type_e, &dev_s, &dev_e, &check); + if (ret < 1) { + continue; + } + line[dir_e] =3D 0; + dash[type_e] =3D 0; + dash[dev_e] =3D 0; + decode_mntname(line + dir_s, dir_e - dir_s); + decode_mntname(dash + dev_s, dev_e - dev_s); + if (devmajor =3D=3D 0) { + /* btrfs reports major number =3D 0 */ + if (strcmp("btrfs", dash + type_s) !=3D 0 || + dev_major_minor(dash + dev_s, &devmajor, &devminor) < 0) { + continue; + } + } + + mount =3D g_new0(FsMount, 1); + mount->dirname =3D g_strdup(line + dir_s); + mount->devtype =3D g_strdup(dash + type_s); + mount->devmajor =3D devmajor; + mount->devminor =3D devminor; + + QTAILQ_INSERT_TAIL(mounts, mount, next); + } + free(line); + + fclose(fp); + return true; +} +#endif + +#if defined(CONFIG_FSFREEZE) + +static char *get_pci_driver(char const *syspath, int pathlen, Error **errp) +{ + char *path; + char *dpath; + char *driver =3D NULL; + char buf[PATH_MAX]; + ssize_t len; + + path =3D g_strndup(syspath, pathlen); + dpath =3D g_strdup_printf("%s/driver", path); + len =3D readlink(dpath, buf, sizeof(buf) - 1); + if (len !=3D -1) { + buf[len] =3D 0; + driver =3D g_path_get_basename(buf); + } + g_free(dpath); + g_free(path); + return driver; +} + +static int compare_uint(const void *_a, const void *_b) +{ + unsigned int a =3D *(unsigned int *)_a; + unsigned int b =3D *(unsigned int *)_b; + + return a < b ? -1 : a > b ? 1 : 0; +} + +/* Walk the specified sysfs and build a sorted list of host or ata numbers= */ +static int build_hosts(char const *syspath, char const *host, bool ata, + unsigned int *hosts, int hosts_max, Error **errp) +{ + char *path; + DIR *dir; + struct dirent *entry; + int i =3D 0; + + path =3D g_strndup(syspath, host - syspath); + dir =3D opendir(path); + if (!dir) { + error_setg_errno(errp, errno, "opendir(\"%s\")", path); + g_free(path); + return -1; + } + + while (i < hosts_max) { + entry =3D readdir(dir); + if (!entry) { + break; + } + if (ata && sscanf(entry->d_name, "ata%d", hosts + i) =3D=3D 1) { + ++i; + } else if (!ata && sscanf(entry->d_name, "host%d", hosts + i) =3D= =3D 1) { + ++i; + } + } + + qsort(hosts, i, sizeof(hosts[0]), compare_uint); + + g_free(path); + closedir(dir); + return i; +} + +/* + * Store disk device info for devices on the PCI bus. + * Returns true if information has been stored, or false for failure. + */ +static bool build_guest_fsinfo_for_pci_dev(char const *syspath, + GuestDiskAddress *disk, + Error **errp) +{ + unsigned int pci[4], host, hosts[8], tgt[3]; + int i, nhosts =3D 0, pcilen; + GuestPCIAddress *pciaddr =3D disk->pci_controller; + bool has_ata =3D false, has_host =3D false, has_tgt =3D false; + char *p, *q, *driver =3D NULL; + bool ret =3D false; + + p =3D strstr(syspath, "/devices/pci"); + if (!p || sscanf(p + 12, "%*x:%*x/%x:%x:%x.%x%n", + pci, pci + 1, pci + 2, pci + 3, &pcilen) < 4) { + g_debug("only pci device is supported: sysfs path '%s'", syspath); + return false; + } + + p +=3D 12 + pcilen; + while (true) { + driver =3D get_pci_driver(syspath, p - syspath, errp); + if (driver && (g_str_equal(driver, "ata_piix") || + g_str_equal(driver, "sym53c8xx") || + g_str_equal(driver, "virtio-pci") || + g_str_equal(driver, "ahci") || + g_str_equal(driver, "nvme"))) { + break; + } + + g_free(driver); + if (sscanf(p, "/%x:%x:%x.%x%n", + pci, pci + 1, pci + 2, pci + 3, &pcilen) =3D=3D = 4) { + p +=3D pcilen; + continue; + } + + g_debug("unsupported driver or sysfs path '%s'", syspath); + return false; + } + + p =3D strstr(syspath, "/target"); + if (p && sscanf(p + 7, "%*u:%*u:%*u/%*u:%u:%u:%u", + tgt, tgt + 1, tgt + 2) =3D=3D 3) { + has_tgt =3D true; + } + + p =3D strstr(syspath, "/ata"); + if (p) { + q =3D p + 4; + has_ata =3D true; + } else { + p =3D strstr(syspath, "/host"); + q =3D p + 5; + } + if (p && sscanf(q, "%u", &host) =3D=3D 1) { + has_host =3D true; + nhosts =3D build_hosts(syspath, p, has_ata, hosts, + ARRAY_SIZE(hosts), errp); + if (nhosts < 0) { + goto cleanup; + } + } + + pciaddr->domain =3D pci[0]; + pciaddr->bus =3D pci[1]; + pciaddr->slot =3D pci[2]; + pciaddr->function =3D pci[3]; + + if (strcmp(driver, "ata_piix") =3D=3D 0) { + /* a host per ide bus, target*:0::0 */ + if (!has_host || !has_tgt) { + g_debug("invalid sysfs path '%s' (driver '%s')", syspath, driv= er); + goto cleanup; + } + for (i =3D 0; i < nhosts; i++) { + if (host =3D=3D hosts[i]) { + disk->bus_type =3D GUEST_DISK_BUS_TYPE_IDE; + disk->bus =3D i; + disk->unit =3D tgt[1]; + break; + } + } + if (i >=3D nhosts) { + g_debug("no host for '%s' (driver '%s')", syspath, driver); + goto cleanup; + } + } else if (strcmp(driver, "sym53c8xx") =3D=3D 0) { + /* scsi(LSI Logic): target*:0::0 */ + if (!has_tgt) { + g_debug("invalid sysfs path '%s' (driver '%s')", syspath, driv= er); + goto cleanup; + } + disk->bus_type =3D GUEST_DISK_BUS_TYPE_SCSI; + disk->unit =3D tgt[1]; + } else if (strcmp(driver, "virtio-pci") =3D=3D 0) { + if (has_tgt) { + /* virtio-scsi: target*:0:0: */ + disk->bus_type =3D GUEST_DISK_BUS_TYPE_SCSI; + disk->unit =3D tgt[2]; + } else { + /* virtio-blk: 1 disk per 1 device */ + disk->bus_type =3D GUEST_DISK_BUS_TYPE_VIRTIO; + } + } else if (strcmp(driver, "ahci") =3D=3D 0) { + /* ahci: 1 host per 1 unit */ + if (!has_host || !has_tgt) { + g_debug("invalid sysfs path '%s' (driver '%s')", syspath, driv= er); + goto cleanup; + } + for (i =3D 0; i < nhosts; i++) { + if (host =3D=3D hosts[i]) { + disk->unit =3D i; + disk->bus_type =3D GUEST_DISK_BUS_TYPE_SATA; + break; + } + } + if (i >=3D nhosts) { + g_debug("no host for '%s' (driver '%s')", syspath, driver); + goto cleanup; + } + } else if (strcmp(driver, "nvme") =3D=3D 0) { + disk->bus_type =3D GUEST_DISK_BUS_TYPE_NVME; + } else { + g_debug("unknown driver '%s' (sysfs path '%s')", driver, syspath); + goto cleanup; + } + + ret =3D true; + +cleanup: + g_free(driver); + return ret; +} + +/* + * Store disk device info for non-PCI virtio devices (for example s390x + * channel I/O devices). Returns true if information has been stored, or + * false for failure. + */ +static bool build_guest_fsinfo_for_nonpci_virtio(char const *syspath, + GuestDiskAddress *disk, + Error **errp) +{ + unsigned int tgt[3]; + char *p; + + if (!strstr(syspath, "/virtio") || !strstr(syspath, "/block")) { + g_debug("Unsupported virtio device '%s'", syspath); + return false; + } + + p =3D strstr(syspath, "/target"); + if (p && sscanf(p + 7, "%*u:%*u:%*u/%*u:%u:%u:%u", + &tgt[0], &tgt[1], &tgt[2]) =3D=3D 3) { + /* virtio-scsi: target*:0:: */ + disk->bus_type =3D GUEST_DISK_BUS_TYPE_SCSI; + disk->bus =3D tgt[0]; + disk->target =3D tgt[1]; + disk->unit =3D tgt[2]; + } else { + /* virtio-blk: 1 disk per 1 device */ + disk->bus_type =3D GUEST_DISK_BUS_TYPE_VIRTIO; + } + + return true; +} + +/* + * Store disk device info for CCW devices (s390x channel I/O devices). + * Returns true if information has been stored, or false for failure. + */ +static bool build_guest_fsinfo_for_ccw_dev(char const *syspath, + GuestDiskAddress *disk, + Error **errp) +{ + unsigned int cssid, ssid, subchno, devno; + char *p; + + p =3D strstr(syspath, "/devices/css"); + if (!p || sscanf(p + 12, "%*x/%x.%x.%x/%*x.%*x.%x/", + &cssid, &ssid, &subchno, &devno) < 4) { + g_debug("could not parse ccw device sysfs path: %s", syspath); + return false; + } + + disk->has_ccw_address =3D true; + disk->ccw_address =3D g_new0(GuestCCWAddress, 1); + disk->ccw_address->cssid =3D cssid; + disk->ccw_address->ssid =3D ssid; + disk->ccw_address->subchno =3D subchno; + disk->ccw_address->devno =3D devno; + + if (strstr(p, "/virtio")) { + build_guest_fsinfo_for_nonpci_virtio(syspath, disk, errp); + } + + return true; +} + +/* Store disk device info specified by @sysfs into @fs */ +static void build_guest_fsinfo_for_real_device(char const *syspath, + GuestFilesystemInfo *fs, + Error **errp) +{ + GuestDiskAddress *disk; + GuestPCIAddress *pciaddr; + bool has_hwinf; +#ifdef CONFIG_LIBUDEV + struct udev *udev =3D NULL; + struct udev_device *udevice =3D NULL; +#endif + + pciaddr =3D g_new0(GuestPCIAddress, 1); + pciaddr->domain =3D -1; /* -1 means field is inv= alid */ + pciaddr->bus =3D -1; + pciaddr->slot =3D -1; + pciaddr->function =3D -1; + + disk =3D g_new0(GuestDiskAddress, 1); + disk->pci_controller =3D pciaddr; + disk->bus_type =3D GUEST_DISK_BUS_TYPE_UNKNOWN; + +#ifdef CONFIG_LIBUDEV + udev =3D udev_new(); + udevice =3D udev_device_new_from_syspath(udev, syspath); + if (udev =3D=3D NULL || udevice =3D=3D NULL) { + g_debug("failed to query udev"); + } else { + const char *devnode, *serial; + devnode =3D udev_device_get_devnode(udevice); + if (devnode !=3D NULL) { + disk->dev =3D g_strdup(devnode); + disk->has_dev =3D true; + } + serial =3D udev_device_get_property_value(udevice, "ID_SERIAL"); + if (serial !=3D NULL && *serial !=3D 0) { + disk->serial =3D g_strdup(serial); + disk->has_serial =3D true; + } + } + + udev_unref(udev); + udev_device_unref(udevice); +#endif + + if (strstr(syspath, "/devices/pci")) { + has_hwinf =3D build_guest_fsinfo_for_pci_dev(syspath, disk, errp); + } else if (strstr(syspath, "/devices/css")) { + has_hwinf =3D build_guest_fsinfo_for_ccw_dev(syspath, disk, errp); + } else if (strstr(syspath, "/virtio")) { + has_hwinf =3D build_guest_fsinfo_for_nonpci_virtio(syspath, disk, = errp); + } else { + g_debug("Unsupported device type for '%s'", syspath); + has_hwinf =3D false; + } + + if (has_hwinf || disk->has_dev || disk->has_serial) { + QAPI_LIST_PREPEND(fs->disk, disk); + } else { + qapi_free_GuestDiskAddress(disk); + } +} + +static void build_guest_fsinfo_for_device(char const *devpath, + GuestFilesystemInfo *fs, + Error **errp); + +/* Store a list of slave devices of virtual volume specified by @syspath i= nto + * @fs */ +static void build_guest_fsinfo_for_virtual_device(char const *syspath, + GuestFilesystemInfo *fs, + Error **errp) +{ + Error *err =3D NULL; + DIR *dir; + char *dirpath; + struct dirent *entry; + + dirpath =3D g_strdup_printf("%s/slaves", syspath); + dir =3D opendir(dirpath); + if (!dir) { + if (errno !=3D ENOENT) { + error_setg_errno(errp, errno, "opendir(\"%s\")", dirpath); + } + g_free(dirpath); + return; + } + + for (;;) { + errno =3D 0; + entry =3D readdir(dir); + if (entry =3D=3D NULL) { + if (errno) { + error_setg_errno(errp, errno, "readdir(\"%s\")", dirpath); + } + break; + } + + if (entry->d_type =3D=3D DT_LNK) { + char *path; + + g_debug(" slave device '%s'", entry->d_name); + path =3D g_strdup_printf("%s/slaves/%s", syspath, entry->d_nam= e); + build_guest_fsinfo_for_device(path, fs, &err); + g_free(path); + + if (err) { + error_propagate(errp, err); + break; + } + } + } + + g_free(dirpath); + closedir(dir); +} + +static bool is_disk_virtual(const char *devpath, Error **errp) +{ + g_autofree char *syspath =3D realpath(devpath, NULL); + + if (!syspath) { + error_setg_errno(errp, errno, "realpath(\"%s\")", devpath); + return false; + } + return strstr(syspath, "/devices/virtual/block/") !=3D NULL; +} + +/* Dispatch to functions for virtual/real device */ +static void build_guest_fsinfo_for_device(char const *devpath, + GuestFilesystemInfo *fs, + Error **errp) +{ + ERRP_GUARD(); + g_autofree char *syspath =3D NULL; + bool is_virtual =3D false; + + syspath =3D realpath(devpath, NULL); + if (!syspath) { + if (errno !=3D ENOENT) { + error_setg_errno(errp, errno, "realpath(\"%s\")", devpath); + return; + } + + /* ENOENT: This devpath may not exist because of container config = */ + if (!fs->name) { + fs->name =3D g_path_get_basename(devpath); + } + return; + } + + if (!fs->name) { + fs->name =3D g_path_get_basename(syspath); + } + + g_debug(" parse sysfs path '%s'", syspath); + is_virtual =3D is_disk_virtual(syspath, errp); + if (*errp !=3D NULL) { + return; + } + if (is_virtual) { + build_guest_fsinfo_for_virtual_device(syspath, fs, errp); + } else { + build_guest_fsinfo_for_real_device(syspath, fs, errp); + } +} + +#ifdef CONFIG_LIBUDEV + +/* + * Wrapper around build_guest_fsinfo_for_device() for getting just + * the disk address. + */ +static GuestDiskAddress *get_disk_address(const char *syspath, Error **err= p) +{ + g_autoptr(GuestFilesystemInfo) fs =3D NULL; + + fs =3D g_new0(GuestFilesystemInfo, 1); + build_guest_fsinfo_for_device(syspath, fs, errp); + if (fs->disk !=3D NULL) { + return g_steal_pointer(&fs->disk->value); + } + return NULL; +} + +static char *get_alias_for_syspath(const char *syspath) +{ + struct udev *udev =3D NULL; + struct udev_device *udevice =3D NULL; + char *ret =3D NULL; + + udev =3D udev_new(); + if (udev =3D=3D NULL) { + g_debug("failed to query udev"); + goto out; + } + udevice =3D udev_device_new_from_syspath(udev, syspath); + if (udevice =3D=3D NULL) { + g_debug("failed to query udev for path: %s", syspath); + goto out; + } else { + const char *alias =3D udev_device_get_property_value( + udevice, "DM_NAME"); + /* + * NULL means there was an error and empty string means there is no + * alias. In case of no alias we return NULL instead of empty stri= ng. + */ + if (alias =3D=3D NULL) { + g_debug("failed to query udev for device alias for: %s", + syspath); + } else if (*alias !=3D 0) { + ret =3D g_strdup(alias); + } + } + +out: + udev_unref(udev); + udev_device_unref(udevice); + return ret; +} + +static char *get_device_for_syspath(const char *syspath) +{ + struct udev *udev =3D NULL; + struct udev_device *udevice =3D NULL; + char *ret =3D NULL; + + udev =3D udev_new(); + if (udev =3D=3D NULL) { + g_debug("failed to query udev"); + goto out; + } + udevice =3D udev_device_new_from_syspath(udev, syspath); + if (udevice =3D=3D NULL) { + g_debug("failed to query udev for path: %s", syspath); + goto out; + } else { + ret =3D g_strdup(udev_device_get_devnode(udevice)); + } + +out: + udev_unref(udev); + udev_device_unref(udevice); + return ret; +} + +static void get_disk_deps(const char *disk_dir, GuestDiskInfo *disk) +{ + g_autofree char *deps_dir =3D NULL; + const gchar *dep; + GDir *dp_deps =3D NULL; + + /* List dependent disks */ + deps_dir =3D g_strdup_printf("%s/slaves", disk_dir); + g_debug(" listing entries in: %s", deps_dir); + dp_deps =3D g_dir_open(deps_dir, 0, NULL); + if (dp_deps =3D=3D NULL) { + g_debug("failed to list entries in %s", deps_dir); + return; + } + disk->has_dependencies =3D true; + while ((dep =3D g_dir_read_name(dp_deps)) !=3D NULL) { + g_autofree char *dep_dir =3D NULL; + char *dev_name; + + /* Add dependent disks */ + dep_dir =3D g_strdup_printf("%s/%s", deps_dir, dep); + dev_name =3D get_device_for_syspath(dep_dir); + if (dev_name !=3D NULL) { + g_debug(" adding dependent device: %s", dev_name); + QAPI_LIST_PREPEND(disk->dependencies, dev_name); + } + } + g_dir_close(dp_deps); +} + +/* + * Detect partitions subdirectory, name is "" or + * "p" + * + * @disk_name -- last component of /sys path (e.g. sda) + * @disk_dir -- sys path of the disk (e.g. /sys/block/sda) + * @disk_dev -- device node of the disk (e.g. /dev/sda) + */ +static GuestDiskInfoList *get_disk_partitions( + GuestDiskInfoList *list, + const char *disk_name, const char *disk_dir, + const char *disk_dev) +{ + GuestDiskInfoList *ret =3D list; + struct dirent *de_disk; + DIR *dp_disk =3D NULL; + size_t len =3D strlen(disk_name); + + dp_disk =3D opendir(disk_dir); + while ((de_disk =3D readdir(dp_disk)) !=3D NULL) { + g_autofree char *partition_dir =3D NULL; + char *dev_name; + GuestDiskInfo *partition; + + if (!(de_disk->d_type & DT_DIR)) { + continue; + } + + if (!(strncmp(disk_name, de_disk->d_name, len) =3D=3D 0 && + ((*(de_disk->d_name + len) =3D=3D 'p' && + isdigit(*(de_disk->d_name + len + 1))) || + isdigit(*(de_disk->d_name + len))))) { + continue; + } + + partition_dir =3D g_strdup_printf("%s/%s", + disk_dir, de_disk->d_name); + dev_name =3D get_device_for_syspath(partition_dir); + if (dev_name =3D=3D NULL) { + g_debug("Failed to get device name for syspath: %s", + disk_dir); + continue; + } + partition =3D g_new0(GuestDiskInfo, 1); + partition->name =3D dev_name; + partition->partition =3D true; + partition->has_dependencies =3D true; + /* Add parent disk as dependent for easier tracking of hierarchy */ + QAPI_LIST_PREPEND(partition->dependencies, g_strdup(disk_dev)); + + QAPI_LIST_PREPEND(ret, partition); + } + closedir(dp_disk); + + return ret; +} + +static void get_nvme_smart(GuestDiskInfo *disk) +{ + int fd; + GuestNVMeSmart *smart; + NvmeSmartLog log =3D {0}; + struct nvme_admin_cmd cmd =3D { + .opcode =3D NVME_ADM_CMD_GET_LOG_PAGE, + .nsid =3D NVME_NSID_BROADCAST, + .addr =3D (uintptr_t)&log, + .data_len =3D sizeof(log), + .cdw10 =3D NVME_LOG_SMART_INFO | (1 << 15) /* RAE bit */ + | (((sizeof(log) >> 2) - 1) << 16) + }; + + fd =3D qga_open_cloexec(disk->name, O_RDONLY, 0); + if (fd =3D=3D -1) { + g_debug("Failed to open device: %s: %s", disk->name, g_strerror(er= rno)); + return; + } + + if (ioctl(fd, NVME_IOCTL_ADMIN_CMD, &cmd)) { + g_debug("Failed to get smart: %s: %s", disk->name, g_strerror(errn= o)); + close(fd); + return; + } + + disk->has_smart =3D true; + disk->smart =3D g_new0(GuestDiskSmart, 1); + disk->smart->type =3D GUEST_DISK_BUS_TYPE_NVME; + + smart =3D &disk->smart->u.nvme; + smart->critical_warning =3D log.critical_warning; + smart->temperature =3D lduw_le_p(&log.temperature); /* unaligned field= */ + smart->available_spare =3D log.available_spare; + smart->available_spare_threshold =3D log.available_spare_threshold; + smart->percentage_used =3D log.percentage_used; + smart->data_units_read_lo =3D le64_to_cpu(log.data_units_read[0]); + smart->data_units_read_hi =3D le64_to_cpu(log.data_units_read[1]); + smart->data_units_written_lo =3D le64_to_cpu(log.data_units_written[0]= ); + smart->data_units_written_hi =3D le64_to_cpu(log.data_units_written[1]= ); + smart->host_read_commands_lo =3D le64_to_cpu(log.host_read_commands[0]= ); + smart->host_read_commands_hi =3D le64_to_cpu(log.host_read_commands[1]= ); + smart->host_write_commands_lo =3D le64_to_cpu(log.host_write_commands[= 0]); + smart->host_write_commands_hi =3D le64_to_cpu(log.host_write_commands[= 1]); + smart->controller_busy_time_lo =3D le64_to_cpu(log.controller_busy_tim= e[0]); + smart->controller_busy_time_hi =3D le64_to_cpu(log.controller_busy_tim= e[1]); + smart->power_cycles_lo =3D le64_to_cpu(log.power_cycles[0]); + smart->power_cycles_hi =3D le64_to_cpu(log.power_cycles[1]); + smart->power_on_hours_lo =3D le64_to_cpu(log.power_on_hours[0]); + smart->power_on_hours_hi =3D le64_to_cpu(log.power_on_hours[1]); + smart->unsafe_shutdowns_lo =3D le64_to_cpu(log.unsafe_shutdowns[0]); + smart->unsafe_shutdowns_hi =3D le64_to_cpu(log.unsafe_shutdowns[1]); + smart->media_errors_lo =3D le64_to_cpu(log.media_errors[0]); + smart->media_errors_hi =3D le64_to_cpu(log.media_errors[1]); + smart->number_of_error_log_entries_lo =3D + le64_to_cpu(log.number_of_error_log_entries[0]); + smart->number_of_error_log_entries_hi =3D + le64_to_cpu(log.number_of_error_log_entries[1]); + + close(fd); +} + +static void get_disk_smart(GuestDiskInfo *disk) +{ + if (disk->has_address + && (disk->address->bus_type =3D=3D GUEST_DISK_BUS_TYPE_NVME)) { + get_nvme_smart(disk); + } +} + +GuestDiskInfoList *qmp_guest_get_disks(Error **errp) +{ + GuestDiskInfoList *ret =3D NULL; + GuestDiskInfo *disk; + DIR *dp =3D NULL; + struct dirent *de =3D NULL; + + g_debug("listing /sys/block directory"); + dp =3D opendir("/sys/block"); + if (dp =3D=3D NULL) { + error_setg_errno(errp, errno, "Can't open directory \"/sys/block\"= "); + return NULL; + } + while ((de =3D readdir(dp)) !=3D NULL) { + g_autofree char *disk_dir =3D NULL, *line =3D NULL, + *size_path =3D NULL; + char *dev_name; + Error *local_err =3D NULL; + if (de->d_type !=3D DT_LNK) { + g_debug(" skipping entry: %s", de->d_name); + continue; + } + + /* Check size and skip zero-sized disks */ + g_debug(" checking disk size"); + size_path =3D g_strdup_printf("/sys/block/%s/size", de->d_name); + if (!g_file_get_contents(size_path, &line, NULL, NULL)) { + g_debug(" failed to read disk size"); + continue; + } + if (g_strcmp0(line, "0\n") =3D=3D 0) { + g_debug(" skipping zero-sized disk"); + continue; + } + + g_debug(" adding %s", de->d_name); + disk_dir =3D g_strdup_printf("/sys/block/%s", de->d_name); + dev_name =3D get_device_for_syspath(disk_dir); + if (dev_name =3D=3D NULL) { + g_debug("Failed to get device name for syspath: %s", + disk_dir); + continue; + } + disk =3D g_new0(GuestDiskInfo, 1); + disk->name =3D dev_name; + disk->partition =3D false; + disk->alias =3D get_alias_for_syspath(disk_dir); + disk->has_alias =3D (disk->alias !=3D NULL); + QAPI_LIST_PREPEND(ret, disk); + + /* Get address for non-virtual devices */ + bool is_virtual =3D is_disk_virtual(disk_dir, &local_err); + if (local_err !=3D NULL) { + g_debug(" failed to check disk path, ignoring error: %s", + error_get_pretty(local_err)); + error_free(local_err); + local_err =3D NULL; + /* Don't try to get the address */ + is_virtual =3D true; + } + if (!is_virtual) { + disk->address =3D get_disk_address(disk_dir, &local_err); + if (local_err !=3D NULL) { + g_debug(" failed to get device info, ignoring error: %s", + error_get_pretty(local_err)); + error_free(local_err); + local_err =3D NULL; + } else if (disk->address !=3D NULL) { + disk->has_address =3D true; + } + } + + get_disk_deps(disk_dir, disk); + get_disk_smart(disk); + ret =3D get_disk_partitions(ret, de->d_name, disk_dir, dev_name); + } + + closedir(dp); + + return ret; +} + +#else + +GuestDiskInfoList *qmp_guest_get_disks(Error **errp) +{ + error_setg(errp, QERR_UNSUPPORTED); + return NULL; +} + +#endif + +/* Return a list of the disk device(s)' info which @mount lies on */ +static GuestFilesystemInfo *build_guest_fsinfo(struct FsMount *mount, + Error **errp) +{ + GuestFilesystemInfo *fs =3D g_malloc0(sizeof(*fs)); + struct statvfs buf; + unsigned long used, nonroot_total, fr_size; + char *devpath =3D g_strdup_printf("/sys/dev/block/%u:%u", + mount->devmajor, mount->devminor); + + fs->mountpoint =3D g_strdup(mount->dirname); + fs->type =3D g_strdup(mount->devtype); + build_guest_fsinfo_for_device(devpath, fs, errp); + + if (statvfs(fs->mountpoint, &buf) =3D=3D 0) { + fr_size =3D buf.f_frsize; + used =3D buf.f_blocks - buf.f_bfree; + nonroot_total =3D used + buf.f_bavail; + fs->used_bytes =3D used * fr_size; + fs->total_bytes =3D nonroot_total * fr_size; + + fs->has_total_bytes =3D true; + fs->has_used_bytes =3D true; + } + + g_free(devpath); + + return fs; +} + +GuestFilesystemInfoList *qmp_guest_get_fsinfo(Error **errp) +{ + FsMountList mounts; + struct FsMount *mount; + GuestFilesystemInfoList *ret =3D NULL; + Error *local_err =3D NULL; + + QTAILQ_INIT(&mounts); + if (!build_fs_mount_list(&mounts, &local_err)) { + error_propagate(errp, local_err); + return NULL; + } + + QTAILQ_FOREACH(mount, &mounts, next) { + g_debug("Building guest fsinfo for '%s'", mount->dirname); + + QAPI_LIST_PREPEND(ret, build_guest_fsinfo(mount, &local_err)); + if (local_err) { + error_propagate(errp, local_err); + qapi_free_GuestFilesystemInfoList(ret); + ret =3D NULL; + break; + } + } + + free_fs_mount_list(&mounts); + return ret; +} + +/* + * Walk list of mounted file systems in the guest, and freeze the ones whi= ch + * are real local file systems. + */ +int64_t qmp_guest_fsfreeze_do_freeze_list(bool has_mountpoints, + strList *mountpoints, + FsMountList mounts, + Error **errp) +{ + struct FsMount *mount; + strList *list; + int fd, ret, i =3D 0; + + QTAILQ_FOREACH_REVERSE(mount, &mounts, next) { + /* To issue fsfreeze in the reverse order of mounts, check if the + * mount is listed in the list here */ + if (has_mountpoints) { + for (list =3D mountpoints; list; list =3D list->next) { + if (strcmp(list->value, mount->dirname) =3D=3D 0) { + break; + } + } + if (!list) { + continue; + } + } + + fd =3D qga_open_cloexec(mount->dirname, O_RDONLY, 0); + if (fd =3D=3D -1) { + error_setg_errno(errp, errno, "failed to open %s", mount->dirn= ame); + return -1; + } + + /* we try to cull filesystems we know won't work in advance, but o= ther + * filesystems may not implement fsfreeze for less obvious reasons. + * these will report EOPNOTSUPP. we simply ignore these when tally= ing + * the number of frozen filesystems. + * if a filesystem is mounted more than once (aka bind mount) a + * consecutive attempt to freeze an already frozen filesystem will + * return EBUSY. + * + * any other error means a failure to freeze a filesystem we + * expect to be freezable, so return an error in those cases + * and return system to thawed state. + */ + ret =3D ioctl(fd, FIFREEZE); + if (ret =3D=3D -1) { + if (errno !=3D EOPNOTSUPP && errno !=3D EBUSY) { + error_setg_errno(errp, errno, "failed to freeze %s", + mount->dirname); + close(fd); + return -1; + } + } else { + i++; + } + close(fd); + } + return i; +} + +int qmp_guest_fsfreeze_do_thaw(Error **errp) +{ + int ret; + FsMountList mounts; + FsMount *mount; + int fd, i =3D 0, logged; + Error *local_err =3D NULL; + + QTAILQ_INIT(&mounts); + if (!build_fs_mount_list(&mounts, &local_err)) { + error_propagate(errp, local_err); + return -1; + } + + QTAILQ_FOREACH(mount, &mounts, next) { + logged =3D false; + fd =3D qga_open_cloexec(mount->dirname, O_RDONLY, 0); + if (fd =3D=3D -1) { + continue; + } + /* we have no way of knowing whether a filesystem was actually unf= rozen + * as a result of a successful call to FITHAW, only that if an err= or + * was returned the filesystem was *not* unfrozen by that particul= ar + * call. + * + * since multiple preceding FIFREEZEs require multiple calls to FI= THAW + * to unfreeze, continuing issuing FITHAW until an error is return= ed, + * in which case either the filesystem is in an unfreezable state,= or, + * more likely, it was thawed previously (and remains so afterward= ). + * + * also, since the most recent successful call is the one that did + * the actual unfreeze, we can use this to provide an accurate cou= nt + * of the number of filesystems unfrozen by guest-fsfreeze-thaw, w= hich + * may * be useful for determining whether a filesystem was unfroz= en + * during the freeze/thaw phase by a process other than qemu-ga. + */ + do { + ret =3D ioctl(fd, FITHAW); + if (ret =3D=3D 0 && !logged) { + i++; + logged =3D true; + } + } while (ret =3D=3D 0); + close(fd); + } + + free_fs_mount_list(&mounts); + + return i; +} +#endif /* CONFIG_FSFREEZE */ + +#if defined(CONFIG_FSTRIM) +/* + * Walk list of mounted file systems in the guest, and trim them. + */ +GuestFilesystemTrimResponse * +qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp) +{ + GuestFilesystemTrimResponse *response; + GuestFilesystemTrimResult *result; + int ret =3D 0; + FsMountList mounts; + struct FsMount *mount; + int fd; + struct fstrim_range r; + + slog("guest-fstrim called"); + + QTAILQ_INIT(&mounts); + if (!build_fs_mount_list(&mounts, errp)) { + return NULL; + } + + response =3D g_malloc0(sizeof(*response)); + + QTAILQ_FOREACH(mount, &mounts, next) { + result =3D g_malloc0(sizeof(*result)); + result->path =3D g_strdup(mount->dirname); + + QAPI_LIST_PREPEND(response->paths, result); + + fd =3D qga_open_cloexec(mount->dirname, O_RDONLY, 0); + if (fd =3D=3D -1) { + result->error =3D g_strdup_printf("failed to open: %s", + strerror(errno)); + result->has_error =3D true; + continue; + } + + /* We try to cull filesystems we know won't work in advance, but o= ther + * filesystems may not implement fstrim for less obvious reasons. + * These will report EOPNOTSUPP; while in some other cases ENOTTY + * will be reported (e.g. CD-ROMs). + * Any other error means an unexpected error. + */ + r.start =3D 0; + r.len =3D -1; + r.minlen =3D has_minimum ? minimum : 0; + ret =3D ioctl(fd, FITRIM, &r); + if (ret =3D=3D -1) { + result->has_error =3D true; + if (errno =3D=3D ENOTTY || errno =3D=3D EOPNOTSUPP) { + result->error =3D g_strdup("trim not supported"); + } else { + result->error =3D g_strdup_printf("failed to trim: %s", + strerror(errno)); + } + close(fd); + continue; + } + + result->has_minimum =3D true; + result->minimum =3D r.minlen; + result->has_trimmed =3D true; + result->trimmed =3D r.len; + close(fd); + } + + free_fs_mount_list(&mounts); + return response; +} +#endif /* CONFIG_FSTRIM */ + + +#define LINUX_SYS_STATE_FILE "/sys/power/state" +#define SUSPEND_SUPPORTED 0 +#define SUSPEND_NOT_SUPPORTED 1 + +typedef enum { + SUSPEND_MODE_DISK =3D 0, + SUSPEND_MODE_RAM =3D 1, + SUSPEND_MODE_HYBRID =3D 2, +} SuspendMode; + +/* + * Executes a command in a child process using g_spawn_sync, + * returning an int >=3D 0 representing the exit status of the + * process. + * + * If the program wasn't found in path, returns -1. + * + * If a problem happened when creating the child process, + * returns -1 and errp is set. + */ +static int run_process_child(const char *command[], Error **errp) +{ + int exit_status, spawn_flag; + GError *g_err =3D NULL; + bool success; + + spawn_flag =3D G_SPAWN_SEARCH_PATH | G_SPAWN_STDOUT_TO_DEV_NULL | + G_SPAWN_STDERR_TO_DEV_NULL; + + success =3D g_spawn_sync(NULL, (char **)command, NULL, spawn_flag, + NULL, NULL, NULL, NULL, + &exit_status, &g_err); + + if (success) { + return WEXITSTATUS(exit_status); + } + + if (g_err && (g_err->code !=3D G_SPAWN_ERROR_NOENT)) { + error_setg(errp, "failed to create child process, error '%s'", + g_err->message); + } + + g_error_free(g_err); + return -1; +} + +static bool systemd_supports_mode(SuspendMode mode, Error **errp) +{ + const char *systemctl_args[3] =3D {"systemd-hibernate", "systemd-suspe= nd", + "systemd-hybrid-sleep"}; + const char *cmd[4] =3D {"systemctl", "status", systemctl_args[mode], N= ULL}; + int status; + + status =3D run_process_child(cmd, errp); + + /* + * systemctl status uses LSB return codes so we can expect + * status > 0 and be ok. To assert if the guest has support + * for the selected suspend mode, status should be < 4. 4 is + * the code for unknown service status, the return value when + * the service does not exist. A common value is status =3D 3 + * (program is not running). + */ + if (status > 0 && status < 4) { + return true; + } + + return false; +} + +static void systemd_suspend(SuspendMode mode, Error **errp) +{ + Error *local_err =3D NULL; + const char *systemctl_args[3] =3D {"hibernate", "suspend", "hybrid-sle= ep"}; + const char *cmd[3] =3D {"systemctl", systemctl_args[mode], NULL}; + int status; + + status =3D run_process_child(cmd, &local_err); + + if (status =3D=3D 0) { + return; + } + + if ((status =3D=3D -1) && !local_err) { + error_setg(errp, "the helper program 'systemctl %s' was not found", + systemctl_args[mode]); + return; + } + + if (local_err) { + error_propagate(errp, local_err); + } else { + error_setg(errp, "the helper program 'systemctl %s' returned an " + "unexpected exit status code (%d)", + systemctl_args[mode], status); + } +} + +static bool pmutils_supports_mode(SuspendMode mode, Error **errp) +{ + Error *local_err =3D NULL; + const char *pmutils_args[3] =3D {"--hibernate", "--suspend", + "--suspend-hybrid"}; + const char *cmd[3] =3D {"pm-is-supported", pmutils_args[mode], NULL}; + int status; + + status =3D run_process_child(cmd, &local_err); + + if (status =3D=3D SUSPEND_SUPPORTED) { + return true; + } + + if ((status =3D=3D -1) && !local_err) { + return false; + } + + if (local_err) { + error_propagate(errp, local_err); + } else { + error_setg(errp, + "the helper program '%s' returned an unexpected exit" + " status code (%d)", "pm-is-supported", status); + } + + return false; +} + +static void pmutils_suspend(SuspendMode mode, Error **errp) +{ + Error *local_err =3D NULL; + const char *pmutils_binaries[3] =3D {"pm-hibernate", "pm-suspend", + "pm-suspend-hybrid"}; + const char *cmd[2] =3D {pmutils_binaries[mode], NULL}; + int status; + + status =3D run_process_child(cmd, &local_err); + + if (status =3D=3D 0) { + return; + } + + if ((status =3D=3D -1) && !local_err) { + error_setg(errp, "the helper program '%s' was not found", + pmutils_binaries[mode]); + return; + } + + if (local_err) { + error_propagate(errp, local_err); + } else { + error_setg(errp, + "the helper program '%s' returned an unexpected exit" + " status code (%d)", pmutils_binaries[mode], status); + } +} + +static bool linux_sys_state_supports_mode(SuspendMode mode, Error **errp) +{ + const char *sysfile_strs[3] =3D {"disk", "mem", NULL}; + const char *sysfile_str =3D sysfile_strs[mode]; + char buf[32]; /* hopefully big enough */ + int fd; + ssize_t ret; + + if (!sysfile_str) { + error_setg(errp, "unknown guest suspend mode"); + return false; + } + + fd =3D open(LINUX_SYS_STATE_FILE, O_RDONLY); + if (fd < 0) { + return false; + } + + ret =3D read(fd, buf, sizeof(buf) - 1); + close(fd); + if (ret <=3D 0) { + return false; + } + buf[ret] =3D '\0'; + + if (strstr(buf, sysfile_str)) { + return true; + } + return false; +} + +static void linux_sys_state_suspend(SuspendMode mode, Error **errp) +{ + Error *local_err =3D NULL; + const char *sysfile_strs[3] =3D {"disk", "mem", NULL}; + const char *sysfile_str =3D sysfile_strs[mode]; + pid_t pid; + int status; + + if (!sysfile_str) { + error_setg(errp, "unknown guest suspend mode"); + return; + } + + pid =3D fork(); + if (!pid) { + /* child */ + int fd; + + setsid(); + reopen_fd_to_null(0); + reopen_fd_to_null(1); + reopen_fd_to_null(2); + + fd =3D open(LINUX_SYS_STATE_FILE, O_WRONLY); + if (fd < 0) { + _exit(EXIT_FAILURE); + } + + if (write(fd, sysfile_str, strlen(sysfile_str)) < 0) { + _exit(EXIT_FAILURE); + } + + _exit(EXIT_SUCCESS); + } else if (pid < 0) { + error_setg_errno(errp, errno, "failed to create child process"); + return; + } + + ga_wait_child(pid, &status, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + if (WEXITSTATUS(status)) { + error_setg(errp, "child process has failed to suspend"); + } + +} + +static void guest_suspend(SuspendMode mode, Error **errp) +{ + Error *local_err =3D NULL; + bool mode_supported =3D false; + + if (systemd_supports_mode(mode, &local_err)) { + mode_supported =3D true; + systemd_suspend(mode, &local_err); + } + + if (!local_err) { + return; + } + + error_free(local_err); + local_err =3D NULL; + + if (pmutils_supports_mode(mode, &local_err)) { + mode_supported =3D true; + pmutils_suspend(mode, &local_err); + } + + if (!local_err) { + return; + } + + error_free(local_err); + local_err =3D NULL; + + if (linux_sys_state_supports_mode(mode, &local_err)) { + mode_supported =3D true; + linux_sys_state_suspend(mode, &local_err); + } + + if (!mode_supported) { + error_free(local_err); + error_setg(errp, + "the requested suspend mode is not supported by the gue= st"); + } else { + error_propagate(errp, local_err); + } +} + +void qmp_guest_suspend_disk(Error **errp) +{ + guest_suspend(SUSPEND_MODE_DISK, errp); +} + +void qmp_guest_suspend_ram(Error **errp) +{ + guest_suspend(SUSPEND_MODE_RAM, errp); +} + +void qmp_guest_suspend_hybrid(Error **errp) +{ + guest_suspend(SUSPEND_MODE_HYBRID, errp); +} + +/* Transfer online/offline status between @vcpu and the guest system. + * + * On input either @errp or *@errp must be NULL. + * + * In system-to-@vcpu direction, the following @vcpu fields are accessed: + * - R: vcpu->logical_id + * - W: vcpu->online + * - W: vcpu->can_offline + * + * In @vcpu-to-system direction, the following @vcpu fields are accessed: + * - R: vcpu->logical_id + * - R: vcpu->online + * + * Written members remain unmodified on error. + */ +static void transfer_vcpu(GuestLogicalProcessor *vcpu, bool sys2vcpu, + char *dirpath, Error **errp) +{ + int fd; + int res; + int dirfd; + static const char fn[] =3D "online"; + + dirfd =3D open(dirpath, O_RDONLY | O_DIRECTORY); + if (dirfd =3D=3D -1) { + error_setg_errno(errp, errno, "open(\"%s\")", dirpath); + return; + } + + fd =3D openat(dirfd, fn, sys2vcpu ? O_RDONLY : O_RDWR); + if (fd =3D=3D -1) { + if (errno !=3D ENOENT) { + error_setg_errno(errp, errno, "open(\"%s/%s\")", dirpath, fn); + } else if (sys2vcpu) { + vcpu->online =3D true; + vcpu->can_offline =3D false; + } else if (!vcpu->online) { + error_setg(errp, "logical processor #%" PRId64 " can't be " + "offlined", vcpu->logical_id); + } /* otherwise pretend successful re-onlining */ + } else { + unsigned char status; + + res =3D pread(fd, &status, 1, 0); + if (res =3D=3D -1) { + error_setg_errno(errp, errno, "pread(\"%s/%s\")", dirpath, fn); + } else if (res =3D=3D 0) { + error_setg(errp, "pread(\"%s/%s\"): unexpected EOF", dirpath, + fn); + } else if (sys2vcpu) { + vcpu->online =3D (status !=3D '0'); + vcpu->can_offline =3D true; + } else if (vcpu->online !=3D (status !=3D '0')) { + status =3D '0' + vcpu->online; + if (pwrite(fd, &status, 1, 0) =3D=3D -1) { + error_setg_errno(errp, errno, "pwrite(\"%s/%s\")", dirpath, + fn); + } + } /* otherwise pretend successful re-(on|off)-lining */ + + res =3D close(fd); + g_assert(res =3D=3D 0); + } + + res =3D close(dirfd); + g_assert(res =3D=3D 0); +} + +GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp) +{ + GuestLogicalProcessorList *head, **tail; + const char *cpu_dir =3D "/sys/devices/system/cpu"; + const gchar *line; + g_autoptr(GDir) cpu_gdir =3D NULL; + Error *local_err =3D NULL; + + head =3D NULL; + tail =3D &head; + cpu_gdir =3D g_dir_open(cpu_dir, 0, NULL); + + if (cpu_gdir =3D=3D NULL) { + error_setg_errno(errp, errno, "failed to list entries: %s", cpu_di= r); + return NULL; + } + + while (local_err =3D=3D NULL && (line =3D g_dir_read_name(cpu_gdir)) != =3D NULL) { + GuestLogicalProcessor *vcpu; + int64_t id; + if (sscanf(line, "cpu%" PRId64, &id)) { + g_autofree char *path =3D g_strdup_printf("/sys/devices/system= /cpu/" + "cpu%" PRId64 "/", id); + vcpu =3D g_malloc0(sizeof *vcpu); + vcpu->logical_id =3D id; + vcpu->has_can_offline =3D true; /* lolspeak ftw */ + transfer_vcpu(vcpu, true, path, &local_err); + QAPI_LIST_APPEND(tail, vcpu); + } + } + + if (local_err =3D=3D NULL) { + /* there's no guest with zero VCPUs */ + g_assert(head !=3D NULL); + return head; + } + + qapi_free_GuestLogicalProcessorList(head); + error_propagate(errp, local_err); + return NULL; +} + +int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp) +{ + int64_t processed; + Error *local_err =3D NULL; + + processed =3D 0; + while (vcpus !=3D NULL) { + char *path =3D g_strdup_printf("/sys/devices/system/cpu/cpu%" PRId= 64 "/", + vcpus->value->logical_id); + + transfer_vcpu(vcpus->value, false, path, &local_err); + g_free(path); + if (local_err !=3D NULL) { + break; + } + ++processed; + vcpus =3D vcpus->next; + } + + if (local_err !=3D NULL) { + if (processed =3D=3D 0) { + error_propagate(errp, local_err); + } else { + error_free(local_err); + } + } + + return processed; +} + +void qmp_guest_set_user_password(const char *username, + const char *password, + bool crypted, + Error **errp) +{ + Error *local_err =3D NULL; + char *passwd_path =3D NULL; + pid_t pid; + int status; + int datafd[2] =3D { -1, -1 }; + char *rawpasswddata =3D NULL; + size_t rawpasswdlen; + char *chpasswddata =3D NULL; + size_t chpasswdlen; + + rawpasswddata =3D (char *)qbase64_decode(password, -1, &rawpasswdlen, = errp); + if (!rawpasswddata) { + return; + } + rawpasswddata =3D g_renew(char, rawpasswddata, rawpasswdlen + 1); + rawpasswddata[rawpasswdlen] =3D '\0'; + + if (strchr(rawpasswddata, '\n')) { + error_setg(errp, "forbidden characters in raw password"); + goto out; + } + + if (strchr(username, '\n') || + strchr(username, ':')) { + error_setg(errp, "forbidden characters in username"); + goto out; + } + + chpasswddata =3D g_strdup_printf("%s:%s\n", username, rawpasswddata); + chpasswdlen =3D strlen(chpasswddata); + + passwd_path =3D g_find_program_in_path("chpasswd"); + + if (!passwd_path) { + error_setg(errp, "cannot find 'passwd' program in PATH"); + goto out; + } + + if (!g_unix_open_pipe(datafd, FD_CLOEXEC, NULL)) { + error_setg(errp, "cannot create pipe FDs"); + goto out; + } + + pid =3D fork(); + if (pid =3D=3D 0) { + close(datafd[1]); + /* child */ + setsid(); + dup2(datafd[0], 0); + reopen_fd_to_null(1); + reopen_fd_to_null(2); + + if (crypted) { + execl(passwd_path, "chpasswd", "-e", NULL); + } else { + execl(passwd_path, "chpasswd", NULL); + } + _exit(EXIT_FAILURE); + } else if (pid < 0) { + error_setg_errno(errp, errno, "failed to create child process"); + goto out; + } + close(datafd[0]); + datafd[0] =3D -1; + + if (qemu_write_full(datafd[1], chpasswddata, chpasswdlen) !=3D chpassw= dlen) { + error_setg_errno(errp, errno, "cannot write new account password"); + goto out; + } + close(datafd[1]); + datafd[1] =3D -1; + + ga_wait_child(pid, &status, &local_err); + if (local_err) { + error_propagate(errp, local_err); + goto out; + } + + if (!WIFEXITED(status)) { + error_setg(errp, "child process has terminated abnormally"); + goto out; + } + + if (WEXITSTATUS(status)) { + error_setg(errp, "child process has failed to set user password"); + goto out; + } + +out: + g_free(chpasswddata); + g_free(rawpasswddata); + g_free(passwd_path); + if (datafd[0] !=3D -1) { + close(datafd[0]); + } + if (datafd[1] !=3D -1) { + close(datafd[1]); + } +} + +static void ga_read_sysfs_file(int dirfd, const char *pathname, char *buf, + int size, Error **errp) +{ + int fd; + int res; + + errno =3D 0; + fd =3D openat(dirfd, pathname, O_RDONLY); + if (fd =3D=3D -1) { + error_setg_errno(errp, errno, "open sysfs file \"%s\"", pathname); + return; + } + + res =3D pread(fd, buf, size, 0); + if (res =3D=3D -1) { + error_setg_errno(errp, errno, "pread sysfs file \"%s\"", pathname); + } else if (res =3D=3D 0) { + error_setg(errp, "pread sysfs file \"%s\": unexpected EOF", pathna= me); + } + close(fd); +} + +static void ga_write_sysfs_file(int dirfd, const char *pathname, + const char *buf, int size, Error **errp) +{ + int fd; + + errno =3D 0; + fd =3D openat(dirfd, pathname, O_WRONLY); + if (fd =3D=3D -1) { + error_setg_errno(errp, errno, "open sysfs file \"%s\"", pathname); + return; + } + + if (pwrite(fd, buf, size, 0) =3D=3D -1) { + error_setg_errno(errp, errno, "pwrite sysfs file \"%s\"", pathname= ); + } + + close(fd); +} + +/* Transfer online/offline status between @mem_blk and the guest system. + * + * On input either @errp or *@errp must be NULL. + * + * In system-to-@mem_blk direction, the following @mem_blk fields are acce= ssed: + * - R: mem_blk->phys_index + * - W: mem_blk->online + * - W: mem_blk->can_offline + * + * In @mem_blk-to-system direction, the following @mem_blk fields are acce= ssed: + * - R: mem_blk->phys_index + * - R: mem_blk->online + *- R: mem_blk->can_offline + * Written members remain unmodified on error. + */ +static void transfer_memory_block(GuestMemoryBlock *mem_blk, bool sys2memb= lk, + GuestMemoryBlockResponse *result, + Error **errp) +{ + char *dirpath; + int dirfd; + char *status; + Error *local_err =3D NULL; + + if (!sys2memblk) { + DIR *dp; + + if (!result) { + error_setg(errp, "Internal error, 'result' should not be NULL"= ); + return; + } + errno =3D 0; + dp =3D opendir("/sys/devices/system/memory/"); + /* if there is no 'memory' directory in sysfs, + * we think this VM does not support online/offline memory block, + * any other solution? + */ + if (!dp) { + if (errno =3D=3D ENOENT) { + result->response =3D + GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_NOT_SUPPORT= ED; + } + goto out1; + } + closedir(dp); + } + + dirpath =3D g_strdup_printf("/sys/devices/system/memory/memory%" PRId6= 4 "/", + mem_blk->phys_index); + dirfd =3D open(dirpath, O_RDONLY | O_DIRECTORY); + if (dirfd =3D=3D -1) { + if (sys2memblk) { + error_setg_errno(errp, errno, "open(\"%s\")", dirpath); + } else { + if (errno =3D=3D ENOENT) { + result->response =3D GUEST_MEMORY_BLOCK_RESPONSE_TYPE_NOT_= FOUND; + } else { + result->response =3D + GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_FAILED; + } + } + g_free(dirpath); + goto out1; + } + g_free(dirpath); + + status =3D g_malloc0(10); + ga_read_sysfs_file(dirfd, "state", status, 10, &local_err); + if (local_err) { + /* treat with sysfs file that not exist in old kernel */ + if (errno =3D=3D ENOENT) { + error_free(local_err); + if (sys2memblk) { + mem_blk->online =3D true; + mem_blk->can_offline =3D false; + } else if (!mem_blk->online) { + result->response =3D + GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_NOT_SUPPORT= ED; + } + } else { + if (sys2memblk) { + error_propagate(errp, local_err); + } else { + error_free(local_err); + result->response =3D + GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_FAILED; + } + } + goto out2; + } + + if (sys2memblk) { + char removable =3D '0'; + + mem_blk->online =3D (strncmp(status, "online", 6) =3D=3D 0); + + ga_read_sysfs_file(dirfd, "removable", &removable, 1, &local_err); + if (local_err) { + /* if no 'removable' file, it doesn't support offline mem blk = */ + if (errno =3D=3D ENOENT) { + error_free(local_err); + mem_blk->can_offline =3D false; + } else { + error_propagate(errp, local_err); + } + } else { + mem_blk->can_offline =3D (removable !=3D '0'); + } + } else { + if (mem_blk->online !=3D (strncmp(status, "online", 6) =3D=3D 0)) { + const char *new_state =3D mem_blk->online ? "online" : "offlin= e"; + + ga_write_sysfs_file(dirfd, "state", new_state, strlen(new_stat= e), + &local_err); + if (local_err) { + error_free(local_err); + result->response =3D + GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_FAILED; + goto out2; + } + + result->response =3D GUEST_MEMORY_BLOCK_RESPONSE_TYPE_SUCCESS; + result->has_error_code =3D false; + } /* otherwise pretend successful re-(on|off)-lining */ + } + g_free(status); + close(dirfd); + return; + +out2: + g_free(status); + close(dirfd); +out1: + if (!sys2memblk) { + result->has_error_code =3D true; + result->error_code =3D errno; + } +} + +GuestMemoryBlockList *qmp_guest_get_memory_blocks(Error **errp) +{ + GuestMemoryBlockList *head, **tail; + Error *local_err =3D NULL; + struct dirent *de; + DIR *dp; + + head =3D NULL; + tail =3D &head; + + dp =3D opendir("/sys/devices/system/memory/"); + if (!dp) { + /* it's ok if this happens to be a system that doesn't expose + * memory blocks via sysfs, but otherwise we should report + * an error + */ + if (errno !=3D ENOENT) { + error_setg_errno(errp, errno, "Can't open directory" + "\"/sys/devices/system/memory/\""); + } + return NULL; + } + + /* Note: the phys_index of memory block may be discontinuous, + * this is because a memblk is the unit of the Sparse Memory design, w= hich + * allows discontinuous memory ranges (ex. NUMA), so here we should + * traverse the memory block directory. + */ + while ((de =3D readdir(dp)) !=3D NULL) { + GuestMemoryBlock *mem_blk; + + if ((strncmp(de->d_name, "memory", 6) !=3D 0) || + !(de->d_type & DT_DIR)) { + continue; + } + + mem_blk =3D g_malloc0(sizeof *mem_blk); + /* The d_name is "memoryXXX", phys_index is block id, same as XXX= */ + mem_blk->phys_index =3D strtoul(&de->d_name[6], NULL, 10); + mem_blk->has_can_offline =3D true; /* lolspeak ftw */ + transfer_memory_block(mem_blk, true, NULL, &local_err); + if (local_err) { + break; + } + + QAPI_LIST_APPEND(tail, mem_blk); + } + + closedir(dp); + if (local_err =3D=3D NULL) { + /* there's no guest with zero memory blocks */ + if (head =3D=3D NULL) { + error_setg(errp, "guest reported zero memory blocks!"); + } + return head; + } + + qapi_free_GuestMemoryBlockList(head); + error_propagate(errp, local_err); + return NULL; +} + +GuestMemoryBlockResponseList * +qmp_guest_set_memory_blocks(GuestMemoryBlockList *mem_blks, Error **errp) +{ + GuestMemoryBlockResponseList *head, **tail; + Error *local_err =3D NULL; + + head =3D NULL; + tail =3D &head; + + while (mem_blks !=3D NULL) { + GuestMemoryBlockResponse *result; + GuestMemoryBlock *current_mem_blk =3D mem_blks->value; + + result =3D g_malloc0(sizeof(*result)); + result->phys_index =3D current_mem_blk->phys_index; + transfer_memory_block(current_mem_blk, false, result, &local_err); + if (local_err) { /* should never happen */ + goto err; + } + + QAPI_LIST_APPEND(tail, result); + mem_blks =3D mem_blks->next; + } + + return head; +err: + qapi_free_GuestMemoryBlockResponseList(head); + error_propagate(errp, local_err); + return NULL; +} + +GuestMemoryBlockInfo *qmp_guest_get_memory_block_info(Error **errp) +{ + Error *local_err =3D NULL; + char *dirpath; + int dirfd; + char *buf; + GuestMemoryBlockInfo *info; + + dirpath =3D g_strdup_printf("/sys/devices/system/memory/"); + dirfd =3D open(dirpath, O_RDONLY | O_DIRECTORY); + if (dirfd =3D=3D -1) { + error_setg_errno(errp, errno, "open(\"%s\")", dirpath); + g_free(dirpath); + return NULL; + } + g_free(dirpath); + + buf =3D g_malloc0(20); + ga_read_sysfs_file(dirfd, "block_size_bytes", buf, 20, &local_err); + close(dirfd); + if (local_err) { + g_free(buf); + error_propagate(errp, local_err); + return NULL; + } + + info =3D g_new0(GuestMemoryBlockInfo, 1); + info->size =3D strtol(buf, NULL, 16); /* the unit is bytes */ + + g_free(buf); + + return info; +} + +#define MAX_NAME_LEN 128 +static GuestDiskStatsInfoList *guest_get_diskstats(Error **errp) +{ +#ifdef CONFIG_LINUX + GuestDiskStatsInfoList *head =3D NULL, **tail =3D &head; + const char *diskstats =3D "/proc/diskstats"; + FILE *fp; + size_t n; + char *line =3D NULL; + + fp =3D fopen(diskstats, "r"); + if (fp =3D=3D NULL) { + error_setg_errno(errp, errno, "open(\"%s\")", diskstats); + return NULL; + } + + while (getline(&line, &n, fp) !=3D -1) { + g_autofree GuestDiskStatsInfo *diskstatinfo =3D NULL; + g_autofree GuestDiskStats *diskstat =3D NULL; + char dev_name[MAX_NAME_LEN]; + unsigned int ios_pgr, tot_ticks, rq_ticks, wr_ticks, dc_ticks, fl_= ticks; + unsigned long rd_ios, rd_merges_or_rd_sec, rd_ticks_or_wr_sec, wr_= ios; + unsigned long wr_merges, rd_sec_or_wr_ios, wr_sec; + unsigned long dc_ios, dc_merges, dc_sec, fl_ios; + unsigned int major, minor; + int i; + + i =3D sscanf(line, "%u %u %s %lu %lu %lu" + "%lu %lu %lu %lu %u %u %u %u" + "%lu %lu %lu %u %lu %u", + &major, &minor, dev_name, + &rd_ios, &rd_merges_or_rd_sec, &rd_sec_or_wr_ios, + &rd_ticks_or_wr_sec, &wr_ios, &wr_merges, &wr_sec, + &wr_ticks, &ios_pgr, &tot_ticks, &rq_ticks, + &dc_ios, &dc_merges, &dc_sec, &dc_ticks, + &fl_ios, &fl_ticks); + + if (i < 7) { + continue; + } + + diskstatinfo =3D g_new0(GuestDiskStatsInfo, 1); + diskstatinfo->name =3D g_strdup(dev_name); + diskstatinfo->major =3D major; + diskstatinfo->minor =3D minor; + + diskstat =3D g_new0(GuestDiskStats, 1); + if (i =3D=3D 7) { + diskstat->has_read_ios =3D true; + diskstat->read_ios =3D rd_ios; + diskstat->has_read_sectors =3D true; + diskstat->read_sectors =3D rd_merges_or_rd_sec; + diskstat->has_write_ios =3D true; + diskstat->write_ios =3D rd_sec_or_wr_ios; + diskstat->has_write_sectors =3D true; + diskstat->write_sectors =3D rd_ticks_or_wr_sec; + } + if (i >=3D 14) { + diskstat->has_read_ios =3D true; + diskstat->read_ios =3D rd_ios; + diskstat->has_read_sectors =3D true; + diskstat->read_sectors =3D rd_sec_or_wr_ios; + diskstat->has_read_merges =3D true; + diskstat->read_merges =3D rd_merges_or_rd_sec; + diskstat->has_read_ticks =3D true; + diskstat->read_ticks =3D rd_ticks_or_wr_sec; + diskstat->has_write_ios =3D true; + diskstat->write_ios =3D wr_ios; + diskstat->has_write_sectors =3D true; + diskstat->write_sectors =3D wr_sec; + diskstat->has_write_merges =3D true; + diskstat->write_merges =3D wr_merges; + diskstat->has_write_ticks =3D true; + diskstat->write_ticks =3D wr_ticks; + diskstat->has_ios_pgr =3D true; + diskstat->ios_pgr =3D ios_pgr; + diskstat->has_total_ticks =3D true; + diskstat->total_ticks =3D tot_ticks; + diskstat->has_weight_ticks =3D true; + diskstat->weight_ticks =3D rq_ticks; + } + if (i >=3D 18) { + diskstat->has_discard_ios =3D true; + diskstat->discard_ios =3D dc_ios; + diskstat->has_discard_merges =3D true; + diskstat->discard_merges =3D dc_merges; + diskstat->has_discard_sectors =3D true; + diskstat->discard_sectors =3D dc_sec; + diskstat->has_discard_ticks =3D true; + diskstat->discard_ticks =3D dc_ticks; + } + if (i >=3D 20) { + diskstat->has_flush_ios =3D true; + diskstat->flush_ios =3D fl_ios; + diskstat->has_flush_ticks =3D true; + diskstat->flush_ticks =3D fl_ticks; + } + + diskstatinfo->stats =3D g_steal_pointer(&diskstat); + QAPI_LIST_APPEND(tail, diskstatinfo); + diskstatinfo =3D NULL; + } + free(line); + fclose(fp); + return head; +#else + g_debug("disk stats reporting available only for Linux"); + return NULL; +#endif +} + +GuestDiskStatsInfoList *qmp_guest_get_diskstats(Error **errp) +{ + return guest_get_diskstats(errp); +} + +GuestCpuStatsList *qmp_guest_get_cpustats(Error **errp) +{ + GuestCpuStatsList *head =3D NULL, **tail =3D &head; + const char *cpustats =3D "/proc/stat"; + int clk_tck =3D sysconf(_SC_CLK_TCK); + FILE *fp; + size_t n; + char *line =3D NULL; + + fp =3D fopen(cpustats, "r"); + if (fp =3D=3D NULL) { + error_setg_errno(errp, errno, "open(\"%s\")", cpustats); + return NULL; + } + + while (getline(&line, &n, fp) !=3D -1) { + GuestCpuStats *cpustat =3D NULL; + GuestLinuxCpuStats *linuxcpustat; + int i; + unsigned long user, system, idle, iowait, irq, softirq, steal, gue= st; + unsigned long nice, guest_nice; + char name[64]; + + i =3D sscanf(line, "%s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu", + name, &user, &nice, &system, &idle, &iowait, &irq, &sof= tirq, + &steal, &guest, &guest_nice); + + /* drop "cpu 1 2 3 ...", get "cpuX 1 2 3 ..." only */ + if ((i =3D=3D EOF) || strncmp(name, "cpu", 3) || (name[3] =3D=3D '= \0')) { + continue; + } + + if (i < 5) { + slog("Parsing cpu stat from %s failed, see \"man proc\"", cpus= tats); + break; + } + + cpustat =3D g_new0(GuestCpuStats, 1); + cpustat->type =3D GUEST_CPU_STATS_TYPE_LINUX; + + linuxcpustat =3D &cpustat->u.q_linux; + linuxcpustat->cpu =3D atoi(&name[3]); + linuxcpustat->user =3D user * 1000 / clk_tck; + linuxcpustat->nice =3D nice * 1000 / clk_tck; + linuxcpustat->system =3D system * 1000 / clk_tck; + linuxcpustat->idle =3D idle * 1000 / clk_tck; + + if (i > 5) { + linuxcpustat->has_iowait =3D true; + linuxcpustat->iowait =3D iowait * 1000 / clk_tck; + } + + if (i > 6) { + linuxcpustat->has_irq =3D true; + linuxcpustat->irq =3D irq * 1000 / clk_tck; + linuxcpustat->has_softirq =3D true; + linuxcpustat->softirq =3D softirq * 1000 / clk_tck; + } + + if (i > 8) { + linuxcpustat->has_steal =3D true; + linuxcpustat->steal =3D steal * 1000 / clk_tck; + } + + if (i > 9) { + linuxcpustat->has_guest =3D true; + linuxcpustat->guest =3D guest * 1000 / clk_tck; + } + + if (i > 10) { + linuxcpustat->has_guest =3D true; + linuxcpustat->guest =3D guest * 1000 / clk_tck; + linuxcpustat->has_guestnice =3D true; + linuxcpustat->guestnice =3D guest_nice * 1000 / clk_tck; + } + + QAPI_LIST_APPEND(tail, cpustat); + } + + free(line); + fclose(fp); + return head; +} diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 954efed01b..0bb8b9e2f3 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -15,7 +15,6 @@ #include #include #include -#include #include "guest-agent-core.h" #include "qga-qapi-commands.h" #include "qapi/error.h" @@ -26,31 +25,12 @@ #include "qemu/base64.h" #include "qemu/cutils.h" #include "commands-common.h" -#include "block/nvme.h" #include "cutils.h" =20 #ifdef HAVE_UTMPX #include #endif =20 -#if defined(__linux__) -#include -#include -#include -#include - -#ifdef CONFIG_LIBUDEV -#include -#endif - -#ifdef FIFREEZE -#define CONFIG_FSFREEZE -#endif -#ifdef FITRIM -#define CONFIG_FSTRIM -#endif -#endif - #ifdef HAVE_GETIFADDRS #include #include @@ -62,7 +42,173 @@ #endif #endif =20 -static void ga_wait_child(pid_t pid, int *status, Error **errp) +#if defined(CONFIG_FSFREEZE) || defined(CONFIG_FSTRIM) +void free_fs_mount_list(FsMountList *mounts) +{ + FsMount *mount, *temp; + + if (!mounts) { + return; + } + + QTAILQ_FOREACH_SAFE(mount, mounts, next, temp) { + QTAILQ_REMOVE(mounts, mount, next); + g_free(mount->dirname); + g_free(mount->devtype); + g_free(mount); + } +} +#endif + +#if defined(CONFIG_FSFREEZE) +typedef enum { + FSFREEZE_HOOK_THAW =3D 0, + FSFREEZE_HOOK_FREEZE, +} FsfreezeHookArg; + +static const char *fsfreeze_hook_arg_string[] =3D { + "thaw", + "freeze", +}; + +static void execute_fsfreeze_hook(FsfreezeHookArg arg, Error **errp) +{ + int status; + pid_t pid; + const char *hook; + const char *arg_str =3D fsfreeze_hook_arg_string[arg]; + Error *local_err =3D NULL; + + hook =3D ga_fsfreeze_hook(ga_state); + if (!hook) { + return; + } + if (access(hook, X_OK) !=3D 0) { + error_setg_errno(errp, errno, "can't access fsfreeze hook '%s'", h= ook); + return; + } + + slog("executing fsfreeze hook with arg '%s'", arg_str); + pid =3D fork(); + if (pid =3D=3D 0) { + setsid(); + reopen_fd_to_null(0); + reopen_fd_to_null(1); + reopen_fd_to_null(2); + + execl(hook, hook, arg_str, NULL); + _exit(EXIT_FAILURE); + } else if (pid < 0) { + error_setg_errno(errp, errno, "failed to create child process"); + return; + } + + ga_wait_child(pid, &status, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + if (!WIFEXITED(status)) { + error_setg(errp, "fsfreeze hook has terminated abnormally"); + return; + } + + status =3D WEXITSTATUS(status); + if (status) { + error_setg(errp, "fsfreeze hook has failed with status %d", status= ); + return; + } +} + +int64_t qmp_guest_fsfreeze_thaw(Error **errp) +{ + int ret; + + ret =3D qmp_guest_fsfreeze_do_thaw(errp); + if (ret >=3D 0) { + ga_unset_frozen(ga_state); + execute_fsfreeze_hook(FSFREEZE_HOOK_THAW, errp); + } else { + ret =3D 0; + } + + return ret; +} + +int64_t qmp_guest_fsfreeze_freeze_list(bool has_mountpoints, + strList *mountpoints, + Error **errp) +{ + int ret; + FsMountList mounts; + Error *local_err =3D NULL; + + slog("guest-fsfreeze called"); + + execute_fsfreeze_hook(FSFREEZE_HOOK_FREEZE, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return -1; + } + + QTAILQ_INIT(&mounts); + if (!build_fs_mount_list(&mounts, &local_err)) { + error_propagate(errp, local_err); + return -1; + } + + /* cannot risk guest agent blocking itself on a write in this state */ + ga_set_frozen(ga_state); + + ret =3D qmp_guest_fsfreeze_do_freeze_list(has_mountpoints, mountpoints, + mounts, errp); + + free_fs_mount_list(&mounts); + /* We may not issue any FIFREEZE here. + * Just unset ga_state here and ready for the next call. + */ + if (ret =3D=3D 0) { + ga_unset_frozen(ga_state); + } else if (ret < 0) { + qmp_guest_fsfreeze_thaw(NULL); + } + return ret; +} + +/* + * Return status of freeze/thaw + */ +GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **errp) +{ + if (ga_is_frozen(ga_state)) { + return GUEST_FSFREEZE_STATUS_FROZEN; + } + + return GUEST_FSFREEZE_STATUS_THAWED; +} + +int64_t qmp_guest_fsfreeze_freeze(Error **errp) +{ + return qmp_guest_fsfreeze_freeze_list(false, NULL, errp); +} + +static void guest_fsfreeze_cleanup(void) +{ + Error *err =3D NULL; + + if (ga_is_frozen(ga_state) =3D=3D GUEST_FSFREEZE_STATUS_FROZEN) { + qmp_guest_fsfreeze_thaw(&err); + if (err) { + slog("failed to clean up frozen filesystems: %s", + error_get_pretty(err)); + error_free(err); + } + } +} +#endif /* CONFIG_FSFREEZE */ + +void ga_wait_child(pid_t pid, int *status, Error **errp) { pid_t rpid; =20 @@ -617,2375 +763,7 @@ void qmp_guest_file_flush(int64_t handle, Error **er= rp) } } =20 -/* linux-specific implementations. avoid this if at all possible. */ -#if defined(__linux__) - -#if defined(CONFIG_FSFREEZE) || defined(CONFIG_FSTRIM) -typedef struct FsMount { - char *dirname; - char *devtype; - unsigned int devmajor, devminor; - QTAILQ_ENTRY(FsMount) next; -} FsMount; - -typedef QTAILQ_HEAD(FsMountList, FsMount) FsMountList; - -static void free_fs_mount_list(FsMountList *mounts) -{ - FsMount *mount, *temp; - - if (!mounts) { - return; - } - - QTAILQ_FOREACH_SAFE(mount, mounts, next, temp) { - QTAILQ_REMOVE(mounts, mount, next); - g_free(mount->dirname); - g_free(mount->devtype); - g_free(mount); - } -} - -static int dev_major_minor(const char *devpath, - unsigned int *devmajor, unsigned int *devminor) -{ - struct stat st; - - *devmajor =3D 0; - *devminor =3D 0; - - if (stat(devpath, &st) < 0) { - slog("failed to stat device file '%s': %s", devpath, strerror(errn= o)); - return -1; - } - if (S_ISDIR(st.st_mode)) { - /* It is bind mount */ - return -2; - } - if (S_ISBLK(st.st_mode)) { - *devmajor =3D major(st.st_rdev); - *devminor =3D minor(st.st_rdev); - return 0; - } - return -1; -} - -/* - * Walk the mount table and build a list of local file systems - */ -static bool build_fs_mount_list_from_mtab(FsMountList *mounts, Error **err= p) -{ - struct mntent *ment; - FsMount *mount; - char const *mtab =3D "/proc/self/mounts"; - FILE *fp; - unsigned int devmajor, devminor; - - fp =3D setmntent(mtab, "r"); - if (!fp) { - error_setg(errp, "failed to open mtab file: '%s'", mtab); - return false; - } - - while ((ment =3D getmntent(fp))) { - /* - * An entry which device name doesn't start with a '/' is - * either a dummy file system or a network file system. - * Add special handling for smbfs and cifs as is done by - * coreutils as well. - */ - if ((ment->mnt_fsname[0] !=3D '/') || - (strcmp(ment->mnt_type, "smbfs") =3D=3D 0) || - (strcmp(ment->mnt_type, "cifs") =3D=3D 0)) { - continue; - } - if (dev_major_minor(ment->mnt_fsname, &devmajor, &devminor) =3D=3D= -2) { - /* Skip bind mounts */ - continue; - } - - mount =3D g_new0(FsMount, 1); - mount->dirname =3D g_strdup(ment->mnt_dir); - mount->devtype =3D g_strdup(ment->mnt_type); - mount->devmajor =3D devmajor; - mount->devminor =3D devminor; - - QTAILQ_INSERT_TAIL(mounts, mount, next); - } - - endmntent(fp); - return true; -} - -static void decode_mntname(char *name, int len) -{ - int i, j =3D 0; - for (i =3D 0; i <=3D len; i++) { - if (name[i] !=3D '\\') { - name[j++] =3D name[i]; - } else if (name[i + 1] =3D=3D '\\') { - name[j++] =3D '\\'; - i++; - } else if (name[i + 1] >=3D '0' && name[i + 1] <=3D '3' && - name[i + 2] >=3D '0' && name[i + 2] <=3D '7' && - name[i + 3] >=3D '0' && name[i + 3] <=3D '7') { - name[j++] =3D (name[i + 1] - '0') * 64 + - (name[i + 2] - '0') * 8 + - (name[i + 3] - '0'); - i +=3D 3; - } else { - name[j++] =3D name[i]; - } - } -} - -static bool build_fs_mount_list(FsMountList *mounts, Error **errp) -{ - FsMount *mount; - char const *mountinfo =3D "/proc/self/mountinfo"; - FILE *fp; - char *line =3D NULL, *dash; - size_t n; - char check; - unsigned int devmajor, devminor; - int ret, dir_s, dir_e, type_s, type_e, dev_s, dev_e; - - fp =3D fopen(mountinfo, "r"); - if (!fp) { - return build_fs_mount_list_from_mtab(mounts, errp); - } - - while (getline(&line, &n, fp) !=3D -1) { - ret =3D sscanf(line, "%*u %*u %u:%u %*s %n%*s%n%c", - &devmajor, &devminor, &dir_s, &dir_e, &check); - if (ret < 3) { - continue; - } - dash =3D strstr(line + dir_e, " - "); - if (!dash) { - continue; - } - ret =3D sscanf(dash, " - %n%*s%n %n%*s%n%c", - &type_s, &type_e, &dev_s, &dev_e, &check); - if (ret < 1) { - continue; - } - line[dir_e] =3D 0; - dash[type_e] =3D 0; - dash[dev_e] =3D 0; - decode_mntname(line + dir_s, dir_e - dir_s); - decode_mntname(dash + dev_s, dev_e - dev_s); - if (devmajor =3D=3D 0) { - /* btrfs reports major number =3D 0 */ - if (strcmp("btrfs", dash + type_s) !=3D 0 || - dev_major_minor(dash + dev_s, &devmajor, &devminor) < 0) { - continue; - } - } - - mount =3D g_new0(FsMount, 1); - mount->dirname =3D g_strdup(line + dir_s); - mount->devtype =3D g_strdup(dash + type_s); - mount->devmajor =3D devmajor; - mount->devminor =3D devminor; - - QTAILQ_INSERT_TAIL(mounts, mount, next); - } - free(line); - - fclose(fp); - return true; -} -#endif - -#if defined(CONFIG_FSFREEZE) - -static char *get_pci_driver(char const *syspath, int pathlen, Error **errp) -{ - char *path; - char *dpath; - char *driver =3D NULL; - char buf[PATH_MAX]; - ssize_t len; - - path =3D g_strndup(syspath, pathlen); - dpath =3D g_strdup_printf("%s/driver", path); - len =3D readlink(dpath, buf, sizeof(buf) - 1); - if (len !=3D -1) { - buf[len] =3D 0; - driver =3D g_path_get_basename(buf); - } - g_free(dpath); - g_free(path); - return driver; -} - -static int compare_uint(const void *_a, const void *_b) -{ - unsigned int a =3D *(unsigned int *)_a; - unsigned int b =3D *(unsigned int *)_b; - - return a < b ? -1 : a > b ? 1 : 0; -} - -/* Walk the specified sysfs and build a sorted list of host or ata numbers= */ -static int build_hosts(char const *syspath, char const *host, bool ata, - unsigned int *hosts, int hosts_max, Error **errp) -{ - char *path; - DIR *dir; - struct dirent *entry; - int i =3D 0; - - path =3D g_strndup(syspath, host - syspath); - dir =3D opendir(path); - if (!dir) { - error_setg_errno(errp, errno, "opendir(\"%s\")", path); - g_free(path); - return -1; - } - - while (i < hosts_max) { - entry =3D readdir(dir); - if (!entry) { - break; - } - if (ata && sscanf(entry->d_name, "ata%d", hosts + i) =3D=3D 1) { - ++i; - } else if (!ata && sscanf(entry->d_name, "host%d", hosts + i) =3D= =3D 1) { - ++i; - } - } - - qsort(hosts, i, sizeof(hosts[0]), compare_uint); - - g_free(path); - closedir(dir); - return i; -} - -/* - * Store disk device info for devices on the PCI bus. - * Returns true if information has been stored, or false for failure. - */ -static bool build_guest_fsinfo_for_pci_dev(char const *syspath, - GuestDiskAddress *disk, - Error **errp) -{ - unsigned int pci[4], host, hosts[8], tgt[3]; - int i, nhosts =3D 0, pcilen; - GuestPCIAddress *pciaddr =3D disk->pci_controller; - bool has_ata =3D false, has_host =3D false, has_tgt =3D false; - char *p, *q, *driver =3D NULL; - bool ret =3D false; - - p =3D strstr(syspath, "/devices/pci"); - if (!p || sscanf(p + 12, "%*x:%*x/%x:%x:%x.%x%n", - pci, pci + 1, pci + 2, pci + 3, &pcilen) < 4) { - g_debug("only pci device is supported: sysfs path '%s'", syspath); - return false; - } - - p +=3D 12 + pcilen; - while (true) { - driver =3D get_pci_driver(syspath, p - syspath, errp); - if (driver && (g_str_equal(driver, "ata_piix") || - g_str_equal(driver, "sym53c8xx") || - g_str_equal(driver, "virtio-pci") || - g_str_equal(driver, "ahci") || - g_str_equal(driver, "nvme"))) { - break; - } - - g_free(driver); - if (sscanf(p, "/%x:%x:%x.%x%n", - pci, pci + 1, pci + 2, pci + 3, &pcilen) =3D=3D = 4) { - p +=3D pcilen; - continue; - } - - g_debug("unsupported driver or sysfs path '%s'", syspath); - return false; - } - - p =3D strstr(syspath, "/target"); - if (p && sscanf(p + 7, "%*u:%*u:%*u/%*u:%u:%u:%u", - tgt, tgt + 1, tgt + 2) =3D=3D 3) { - has_tgt =3D true; - } - - p =3D strstr(syspath, "/ata"); - if (p) { - q =3D p + 4; - has_ata =3D true; - } else { - p =3D strstr(syspath, "/host"); - q =3D p + 5; - } - if (p && sscanf(q, "%u", &host) =3D=3D 1) { - has_host =3D true; - nhosts =3D build_hosts(syspath, p, has_ata, hosts, - ARRAY_SIZE(hosts), errp); - if (nhosts < 0) { - goto cleanup; - } - } - - pciaddr->domain =3D pci[0]; - pciaddr->bus =3D pci[1]; - pciaddr->slot =3D pci[2]; - pciaddr->function =3D pci[3]; - - if (strcmp(driver, "ata_piix") =3D=3D 0) { - /* a host per ide bus, target*:0::0 */ - if (!has_host || !has_tgt) { - g_debug("invalid sysfs path '%s' (driver '%s')", syspath, driv= er); - goto cleanup; - } - for (i =3D 0; i < nhosts; i++) { - if (host =3D=3D hosts[i]) { - disk->bus_type =3D GUEST_DISK_BUS_TYPE_IDE; - disk->bus =3D i; - disk->unit =3D tgt[1]; - break; - } - } - if (i >=3D nhosts) { - g_debug("no host for '%s' (driver '%s')", syspath, driver); - goto cleanup; - } - } else if (strcmp(driver, "sym53c8xx") =3D=3D 0) { - /* scsi(LSI Logic): target*:0::0 */ - if (!has_tgt) { - g_debug("invalid sysfs path '%s' (driver '%s')", syspath, driv= er); - goto cleanup; - } - disk->bus_type =3D GUEST_DISK_BUS_TYPE_SCSI; - disk->unit =3D tgt[1]; - } else if (strcmp(driver, "virtio-pci") =3D=3D 0) { - if (has_tgt) { - /* virtio-scsi: target*:0:0: */ - disk->bus_type =3D GUEST_DISK_BUS_TYPE_SCSI; - disk->unit =3D tgt[2]; - } else { - /* virtio-blk: 1 disk per 1 device */ - disk->bus_type =3D GUEST_DISK_BUS_TYPE_VIRTIO; - } - } else if (strcmp(driver, "ahci") =3D=3D 0) { - /* ahci: 1 host per 1 unit */ - if (!has_host || !has_tgt) { - g_debug("invalid sysfs path '%s' (driver '%s')", syspath, driv= er); - goto cleanup; - } - for (i =3D 0; i < nhosts; i++) { - if (host =3D=3D hosts[i]) { - disk->unit =3D i; - disk->bus_type =3D GUEST_DISK_BUS_TYPE_SATA; - break; - } - } - if (i >=3D nhosts) { - g_debug("no host for '%s' (driver '%s')", syspath, driver); - goto cleanup; - } - } else if (strcmp(driver, "nvme") =3D=3D 0) { - disk->bus_type =3D GUEST_DISK_BUS_TYPE_NVME; - } else { - g_debug("unknown driver '%s' (sysfs path '%s')", driver, syspath); - goto cleanup; - } - - ret =3D true; - -cleanup: - g_free(driver); - return ret; -} - -/* - * Store disk device info for non-PCI virtio devices (for example s390x - * channel I/O devices). Returns true if information has been stored, or - * false for failure. - */ -static bool build_guest_fsinfo_for_nonpci_virtio(char const *syspath, - GuestDiskAddress *disk, - Error **errp) -{ - unsigned int tgt[3]; - char *p; - - if (!strstr(syspath, "/virtio") || !strstr(syspath, "/block")) { - g_debug("Unsupported virtio device '%s'", syspath); - return false; - } - - p =3D strstr(syspath, "/target"); - if (p && sscanf(p + 7, "%*u:%*u:%*u/%*u:%u:%u:%u", - &tgt[0], &tgt[1], &tgt[2]) =3D=3D 3) { - /* virtio-scsi: target*:0:: */ - disk->bus_type =3D GUEST_DISK_BUS_TYPE_SCSI; - disk->bus =3D tgt[0]; - disk->target =3D tgt[1]; - disk->unit =3D tgt[2]; - } else { - /* virtio-blk: 1 disk per 1 device */ - disk->bus_type =3D GUEST_DISK_BUS_TYPE_VIRTIO; - } - - return true; -} - -/* - * Store disk device info for CCW devices (s390x channel I/O devices). - * Returns true if information has been stored, or false for failure. - */ -static bool build_guest_fsinfo_for_ccw_dev(char const *syspath, - GuestDiskAddress *disk, - Error **errp) -{ - unsigned int cssid, ssid, subchno, devno; - char *p; - - p =3D strstr(syspath, "/devices/css"); - if (!p || sscanf(p + 12, "%*x/%x.%x.%x/%*x.%*x.%x/", - &cssid, &ssid, &subchno, &devno) < 4) { - g_debug("could not parse ccw device sysfs path: %s", syspath); - return false; - } - - disk->has_ccw_address =3D true; - disk->ccw_address =3D g_new0(GuestCCWAddress, 1); - disk->ccw_address->cssid =3D cssid; - disk->ccw_address->ssid =3D ssid; - disk->ccw_address->subchno =3D subchno; - disk->ccw_address->devno =3D devno; - - if (strstr(p, "/virtio")) { - build_guest_fsinfo_for_nonpci_virtio(syspath, disk, errp); - } - - return true; -} - -/* Store disk device info specified by @sysfs into @fs */ -static void build_guest_fsinfo_for_real_device(char const *syspath, - GuestFilesystemInfo *fs, - Error **errp) -{ - GuestDiskAddress *disk; - GuestPCIAddress *pciaddr; - bool has_hwinf; -#ifdef CONFIG_LIBUDEV - struct udev *udev =3D NULL; - struct udev_device *udevice =3D NULL; -#endif - - pciaddr =3D g_new0(GuestPCIAddress, 1); - pciaddr->domain =3D -1; /* -1 means field is inv= alid */ - pciaddr->bus =3D -1; - pciaddr->slot =3D -1; - pciaddr->function =3D -1; - - disk =3D g_new0(GuestDiskAddress, 1); - disk->pci_controller =3D pciaddr; - disk->bus_type =3D GUEST_DISK_BUS_TYPE_UNKNOWN; - -#ifdef CONFIG_LIBUDEV - udev =3D udev_new(); - udevice =3D udev_device_new_from_syspath(udev, syspath); - if (udev =3D=3D NULL || udevice =3D=3D NULL) { - g_debug("failed to query udev"); - } else { - const char *devnode, *serial; - devnode =3D udev_device_get_devnode(udevice); - if (devnode !=3D NULL) { - disk->dev =3D g_strdup(devnode); - disk->has_dev =3D true; - } - serial =3D udev_device_get_property_value(udevice, "ID_SERIAL"); - if (serial !=3D NULL && *serial !=3D 0) { - disk->serial =3D g_strdup(serial); - disk->has_serial =3D true; - } - } - - udev_unref(udev); - udev_device_unref(udevice); -#endif - - if (strstr(syspath, "/devices/pci")) { - has_hwinf =3D build_guest_fsinfo_for_pci_dev(syspath, disk, errp); - } else if (strstr(syspath, "/devices/css")) { - has_hwinf =3D build_guest_fsinfo_for_ccw_dev(syspath, disk, errp); - } else if (strstr(syspath, "/virtio")) { - has_hwinf =3D build_guest_fsinfo_for_nonpci_virtio(syspath, disk, = errp); - } else { - g_debug("Unsupported device type for '%s'", syspath); - has_hwinf =3D false; - } - - if (has_hwinf || disk->has_dev || disk->has_serial) { - QAPI_LIST_PREPEND(fs->disk, disk); - } else { - qapi_free_GuestDiskAddress(disk); - } -} - -static void build_guest_fsinfo_for_device(char const *devpath, - GuestFilesystemInfo *fs, - Error **errp); - -/* Store a list of slave devices of virtual volume specified by @syspath i= nto - * @fs */ -static void build_guest_fsinfo_for_virtual_device(char const *syspath, - GuestFilesystemInfo *fs, - Error **errp) -{ - Error *err =3D NULL; - DIR *dir; - char *dirpath; - struct dirent *entry; - - dirpath =3D g_strdup_printf("%s/slaves", syspath); - dir =3D opendir(dirpath); - if (!dir) { - if (errno !=3D ENOENT) { - error_setg_errno(errp, errno, "opendir(\"%s\")", dirpath); - } - g_free(dirpath); - return; - } - - for (;;) { - errno =3D 0; - entry =3D readdir(dir); - if (entry =3D=3D NULL) { - if (errno) { - error_setg_errno(errp, errno, "readdir(\"%s\")", dirpath); - } - break; - } - - if (entry->d_type =3D=3D DT_LNK) { - char *path; - - g_debug(" slave device '%s'", entry->d_name); - path =3D g_strdup_printf("%s/slaves/%s", syspath, entry->d_nam= e); - build_guest_fsinfo_for_device(path, fs, &err); - g_free(path); - - if (err) { - error_propagate(errp, err); - break; - } - } - } - - g_free(dirpath); - closedir(dir); -} - -static bool is_disk_virtual(const char *devpath, Error **errp) -{ - g_autofree char *syspath =3D realpath(devpath, NULL); - - if (!syspath) { - error_setg_errno(errp, errno, "realpath(\"%s\")", devpath); - return false; - } - return strstr(syspath, "/devices/virtual/block/") !=3D NULL; -} - -/* Dispatch to functions for virtual/real device */ -static void build_guest_fsinfo_for_device(char const *devpath, - GuestFilesystemInfo *fs, - Error **errp) -{ - ERRP_GUARD(); - g_autofree char *syspath =3D NULL; - bool is_virtual =3D false; - - syspath =3D realpath(devpath, NULL); - if (!syspath) { - if (errno !=3D ENOENT) { - error_setg_errno(errp, errno, "realpath(\"%s\")", devpath); - return; - } - - /* ENOENT: This devpath may not exist because of container config = */ - if (!fs->name) { - fs->name =3D g_path_get_basename(devpath); - } - return; - } - - if (!fs->name) { - fs->name =3D g_path_get_basename(syspath); - } - - g_debug(" parse sysfs path '%s'", syspath); - is_virtual =3D is_disk_virtual(syspath, errp); - if (*errp !=3D NULL) { - return; - } - if (is_virtual) { - build_guest_fsinfo_for_virtual_device(syspath, fs, errp); - } else { - build_guest_fsinfo_for_real_device(syspath, fs, errp); - } -} - -#ifdef CONFIG_LIBUDEV - -/* - * Wrapper around build_guest_fsinfo_for_device() for getting just - * the disk address. - */ -static GuestDiskAddress *get_disk_address(const char *syspath, Error **err= p) -{ - g_autoptr(GuestFilesystemInfo) fs =3D NULL; - - fs =3D g_new0(GuestFilesystemInfo, 1); - build_guest_fsinfo_for_device(syspath, fs, errp); - if (fs->disk !=3D NULL) { - return g_steal_pointer(&fs->disk->value); - } - return NULL; -} - -static char *get_alias_for_syspath(const char *syspath) -{ - struct udev *udev =3D NULL; - struct udev_device *udevice =3D NULL; - char *ret =3D NULL; - - udev =3D udev_new(); - if (udev =3D=3D NULL) { - g_debug("failed to query udev"); - goto out; - } - udevice =3D udev_device_new_from_syspath(udev, syspath); - if (udevice =3D=3D NULL) { - g_debug("failed to query udev for path: %s", syspath); - goto out; - } else { - const char *alias =3D udev_device_get_property_value( - udevice, "DM_NAME"); - /* - * NULL means there was an error and empty string means there is no - * alias. In case of no alias we return NULL instead of empty stri= ng. - */ - if (alias =3D=3D NULL) { - g_debug("failed to query udev for device alias for: %s", - syspath); - } else if (*alias !=3D 0) { - ret =3D g_strdup(alias); - } - } - -out: - udev_unref(udev); - udev_device_unref(udevice); - return ret; -} - -static char *get_device_for_syspath(const char *syspath) -{ - struct udev *udev =3D NULL; - struct udev_device *udevice =3D NULL; - char *ret =3D NULL; - - udev =3D udev_new(); - if (udev =3D=3D NULL) { - g_debug("failed to query udev"); - goto out; - } - udevice =3D udev_device_new_from_syspath(udev, syspath); - if (udevice =3D=3D NULL) { - g_debug("failed to query udev for path: %s", syspath); - goto out; - } else { - ret =3D g_strdup(udev_device_get_devnode(udevice)); - } - -out: - udev_unref(udev); - udev_device_unref(udevice); - return ret; -} - -static void get_disk_deps(const char *disk_dir, GuestDiskInfo *disk) -{ - g_autofree char *deps_dir =3D NULL; - const gchar *dep; - GDir *dp_deps =3D NULL; - - /* List dependent disks */ - deps_dir =3D g_strdup_printf("%s/slaves", disk_dir); - g_debug(" listing entries in: %s", deps_dir); - dp_deps =3D g_dir_open(deps_dir, 0, NULL); - if (dp_deps =3D=3D NULL) { - g_debug("failed to list entries in %s", deps_dir); - return; - } - disk->has_dependencies =3D true; - while ((dep =3D g_dir_read_name(dp_deps)) !=3D NULL) { - g_autofree char *dep_dir =3D NULL; - char *dev_name; - - /* Add dependent disks */ - dep_dir =3D g_strdup_printf("%s/%s", deps_dir, dep); - dev_name =3D get_device_for_syspath(dep_dir); - if (dev_name !=3D NULL) { - g_debug(" adding dependent device: %s", dev_name); - QAPI_LIST_PREPEND(disk->dependencies, dev_name); - } - } - g_dir_close(dp_deps); -} - -/* - * Detect partitions subdirectory, name is "" or - * "p" - * - * @disk_name -- last component of /sys path (e.g. sda) - * @disk_dir -- sys path of the disk (e.g. /sys/block/sda) - * @disk_dev -- device node of the disk (e.g. /dev/sda) - */ -static GuestDiskInfoList *get_disk_partitions( - GuestDiskInfoList *list, - const char *disk_name, const char *disk_dir, - const char *disk_dev) -{ - GuestDiskInfoList *ret =3D list; - struct dirent *de_disk; - DIR *dp_disk =3D NULL; - size_t len =3D strlen(disk_name); - - dp_disk =3D opendir(disk_dir); - while ((de_disk =3D readdir(dp_disk)) !=3D NULL) { - g_autofree char *partition_dir =3D NULL; - char *dev_name; - GuestDiskInfo *partition; - - if (!(de_disk->d_type & DT_DIR)) { - continue; - } - - if (!(strncmp(disk_name, de_disk->d_name, len) =3D=3D 0 && - ((*(de_disk->d_name + len) =3D=3D 'p' && - isdigit(*(de_disk->d_name + len + 1))) || - isdigit(*(de_disk->d_name + len))))) { - continue; - } - - partition_dir =3D g_strdup_printf("%s/%s", - disk_dir, de_disk->d_name); - dev_name =3D get_device_for_syspath(partition_dir); - if (dev_name =3D=3D NULL) { - g_debug("Failed to get device name for syspath: %s", - disk_dir); - continue; - } - partition =3D g_new0(GuestDiskInfo, 1); - partition->name =3D dev_name; - partition->partition =3D true; - partition->has_dependencies =3D true; - /* Add parent disk as dependent for easier tracking of hierarchy */ - QAPI_LIST_PREPEND(partition->dependencies, g_strdup(disk_dev)); - - QAPI_LIST_PREPEND(ret, partition); - } - closedir(dp_disk); - - return ret; -} - -static void get_nvme_smart(GuestDiskInfo *disk) -{ - int fd; - GuestNVMeSmart *smart; - NvmeSmartLog log =3D {0}; - struct nvme_admin_cmd cmd =3D { - .opcode =3D NVME_ADM_CMD_GET_LOG_PAGE, - .nsid =3D NVME_NSID_BROADCAST, - .addr =3D (uintptr_t)&log, - .data_len =3D sizeof(log), - .cdw10 =3D NVME_LOG_SMART_INFO | (1 << 15) /* RAE bit */ - | (((sizeof(log) >> 2) - 1) << 16) - }; - - fd =3D qga_open_cloexec(disk->name, O_RDONLY, 0); - if (fd =3D=3D -1) { - g_debug("Failed to open device: %s: %s", disk->name, g_strerror(er= rno)); - return; - } - - if (ioctl(fd, NVME_IOCTL_ADMIN_CMD, &cmd)) { - g_debug("Failed to get smart: %s: %s", disk->name, g_strerror(errn= o)); - close(fd); - return; - } - - disk->has_smart =3D true; - disk->smart =3D g_new0(GuestDiskSmart, 1); - disk->smart->type =3D GUEST_DISK_BUS_TYPE_NVME; - - smart =3D &disk->smart->u.nvme; - smart->critical_warning =3D log.critical_warning; - smart->temperature =3D lduw_le_p(&log.temperature); /* unaligned field= */ - smart->available_spare =3D log.available_spare; - smart->available_spare_threshold =3D log.available_spare_threshold; - smart->percentage_used =3D log.percentage_used; - smart->data_units_read_lo =3D le64_to_cpu(log.data_units_read[0]); - smart->data_units_read_hi =3D le64_to_cpu(log.data_units_read[1]); - smart->data_units_written_lo =3D le64_to_cpu(log.data_units_written[0]= ); - smart->data_units_written_hi =3D le64_to_cpu(log.data_units_written[1]= ); - smart->host_read_commands_lo =3D le64_to_cpu(log.host_read_commands[0]= ); - smart->host_read_commands_hi =3D le64_to_cpu(log.host_read_commands[1]= ); - smart->host_write_commands_lo =3D le64_to_cpu(log.host_write_commands[= 0]); - smart->host_write_commands_hi =3D le64_to_cpu(log.host_write_commands[= 1]); - smart->controller_busy_time_lo =3D le64_to_cpu(log.controller_busy_tim= e[0]); - smart->controller_busy_time_hi =3D le64_to_cpu(log.controller_busy_tim= e[1]); - smart->power_cycles_lo =3D le64_to_cpu(log.power_cycles[0]); - smart->power_cycles_hi =3D le64_to_cpu(log.power_cycles[1]); - smart->power_on_hours_lo =3D le64_to_cpu(log.power_on_hours[0]); - smart->power_on_hours_hi =3D le64_to_cpu(log.power_on_hours[1]); - smart->unsafe_shutdowns_lo =3D le64_to_cpu(log.unsafe_shutdowns[0]); - smart->unsafe_shutdowns_hi =3D le64_to_cpu(log.unsafe_shutdowns[1]); - smart->media_errors_lo =3D le64_to_cpu(log.media_errors[0]); - smart->media_errors_hi =3D le64_to_cpu(log.media_errors[1]); - smart->number_of_error_log_entries_lo =3D - le64_to_cpu(log.number_of_error_log_entries[0]); - smart->number_of_error_log_entries_hi =3D - le64_to_cpu(log.number_of_error_log_entries[1]); - - close(fd); -} - -static void get_disk_smart(GuestDiskInfo *disk) -{ - if (disk->has_address - && (disk->address->bus_type =3D=3D GUEST_DISK_BUS_TYPE_NVME)) { - get_nvme_smart(disk); - } -} - -GuestDiskInfoList *qmp_guest_get_disks(Error **errp) -{ - GuestDiskInfoList *ret =3D NULL; - GuestDiskInfo *disk; - DIR *dp =3D NULL; - struct dirent *de =3D NULL; - - g_debug("listing /sys/block directory"); - dp =3D opendir("/sys/block"); - if (dp =3D=3D NULL) { - error_setg_errno(errp, errno, "Can't open directory \"/sys/block\"= "); - return NULL; - } - while ((de =3D readdir(dp)) !=3D NULL) { - g_autofree char *disk_dir =3D NULL, *line =3D NULL, - *size_path =3D NULL; - char *dev_name; - Error *local_err =3D NULL; - if (de->d_type !=3D DT_LNK) { - g_debug(" skipping entry: %s", de->d_name); - continue; - } - - /* Check size and skip zero-sized disks */ - g_debug(" checking disk size"); - size_path =3D g_strdup_printf("/sys/block/%s/size", de->d_name); - if (!g_file_get_contents(size_path, &line, NULL, NULL)) { - g_debug(" failed to read disk size"); - continue; - } - if (g_strcmp0(line, "0\n") =3D=3D 0) { - g_debug(" skipping zero-sized disk"); - continue; - } - - g_debug(" adding %s", de->d_name); - disk_dir =3D g_strdup_printf("/sys/block/%s", de->d_name); - dev_name =3D get_device_for_syspath(disk_dir); - if (dev_name =3D=3D NULL) { - g_debug("Failed to get device name for syspath: %s", - disk_dir); - continue; - } - disk =3D g_new0(GuestDiskInfo, 1); - disk->name =3D dev_name; - disk->partition =3D false; - disk->alias =3D get_alias_for_syspath(disk_dir); - disk->has_alias =3D (disk->alias !=3D NULL); - QAPI_LIST_PREPEND(ret, disk); - - /* Get address for non-virtual devices */ - bool is_virtual =3D is_disk_virtual(disk_dir, &local_err); - if (local_err !=3D NULL) { - g_debug(" failed to check disk path, ignoring error: %s", - error_get_pretty(local_err)); - error_free(local_err); - local_err =3D NULL; - /* Don't try to get the address */ - is_virtual =3D true; - } - if (!is_virtual) { - disk->address =3D get_disk_address(disk_dir, &local_err); - if (local_err !=3D NULL) { - g_debug(" failed to get device info, ignoring error: %s", - error_get_pretty(local_err)); - error_free(local_err); - local_err =3D NULL; - } else if (disk->address !=3D NULL) { - disk->has_address =3D true; - } - } - - get_disk_deps(disk_dir, disk); - get_disk_smart(disk); - ret =3D get_disk_partitions(ret, de->d_name, disk_dir, dev_name); - } - - closedir(dp); - - return ret; -} - -#else - -GuestDiskInfoList *qmp_guest_get_disks(Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); - return NULL; -} - -#endif - -/* Return a list of the disk device(s)' info which @mount lies on */ -static GuestFilesystemInfo *build_guest_fsinfo(struct FsMount *mount, - Error **errp) -{ - GuestFilesystemInfo *fs =3D g_malloc0(sizeof(*fs)); - struct statvfs buf; - unsigned long used, nonroot_total, fr_size; - char *devpath =3D g_strdup_printf("/sys/dev/block/%u:%u", - mount->devmajor, mount->devminor); - - fs->mountpoint =3D g_strdup(mount->dirname); - fs->type =3D g_strdup(mount->devtype); - build_guest_fsinfo_for_device(devpath, fs, errp); - - if (statvfs(fs->mountpoint, &buf) =3D=3D 0) { - fr_size =3D buf.f_frsize; - used =3D buf.f_blocks - buf.f_bfree; - nonroot_total =3D used + buf.f_bavail; - fs->used_bytes =3D used * fr_size; - fs->total_bytes =3D nonroot_total * fr_size; - - fs->has_total_bytes =3D true; - fs->has_used_bytes =3D true; - } - - g_free(devpath); - - return fs; -} - -GuestFilesystemInfoList *qmp_guest_get_fsinfo(Error **errp) -{ - FsMountList mounts; - struct FsMount *mount; - GuestFilesystemInfoList *ret =3D NULL; - Error *local_err =3D NULL; - - QTAILQ_INIT(&mounts); - if (!build_fs_mount_list(&mounts, &local_err)) { - error_propagate(errp, local_err); - return NULL; - } - - QTAILQ_FOREACH(mount, &mounts, next) { - g_debug("Building guest fsinfo for '%s'", mount->dirname); - - QAPI_LIST_PREPEND(ret, build_guest_fsinfo(mount, &local_err)); - if (local_err) { - error_propagate(errp, local_err); - qapi_free_GuestFilesystemInfoList(ret); - ret =3D NULL; - break; - } - } - - free_fs_mount_list(&mounts); - return ret; -} - - -typedef enum { - FSFREEZE_HOOK_THAW =3D 0, - FSFREEZE_HOOK_FREEZE, -} FsfreezeHookArg; - -static const char *fsfreeze_hook_arg_string[] =3D { - "thaw", - "freeze", -}; - -static void execute_fsfreeze_hook(FsfreezeHookArg arg, Error **errp) -{ - int status; - pid_t pid; - const char *hook; - const char *arg_str =3D fsfreeze_hook_arg_string[arg]; - Error *local_err =3D NULL; - - hook =3D ga_fsfreeze_hook(ga_state); - if (!hook) { - return; - } - if (access(hook, X_OK) !=3D 0) { - error_setg_errno(errp, errno, "can't access fsfreeze hook '%s'", h= ook); - return; - } - - slog("executing fsfreeze hook with arg '%s'", arg_str); - pid =3D fork(); - if (pid =3D=3D 0) { - setsid(); - reopen_fd_to_null(0); - reopen_fd_to_null(1); - reopen_fd_to_null(2); - - execl(hook, hook, arg_str, NULL); - _exit(EXIT_FAILURE); - } else if (pid < 0) { - error_setg_errno(errp, errno, "failed to create child process"); - return; - } - - ga_wait_child(pid, &status, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - - if (!WIFEXITED(status)) { - error_setg(errp, "fsfreeze hook has terminated abnormally"); - return; - } - - status =3D WEXITSTATUS(status); - if (status) { - error_setg(errp, "fsfreeze hook has failed with status %d", status= ); - return; - } -} - -/* - * Return status of freeze/thaw - */ -GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **errp) -{ - if (ga_is_frozen(ga_state)) { - return GUEST_FSFREEZE_STATUS_FROZEN; - } - - return GUEST_FSFREEZE_STATUS_THAWED; -} - -int64_t qmp_guest_fsfreeze_freeze(Error **errp) -{ - return qmp_guest_fsfreeze_freeze_list(false, NULL, errp); -} - -/* - * Walk list of mounted file systems in the guest, and freeze the ones whi= ch - * are real local file systems. - */ -int64_t qmp_guest_fsfreeze_freeze_list(bool has_mountpoints, - strList *mountpoints, - Error **errp) -{ - int ret =3D 0, i =3D 0; - strList *list; - FsMountList mounts; - struct FsMount *mount; - Error *local_err =3D NULL; - int fd; - - slog("guest-fsfreeze called"); - - execute_fsfreeze_hook(FSFREEZE_HOOK_FREEZE, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return -1; - } - - QTAILQ_INIT(&mounts); - if (!build_fs_mount_list(&mounts, &local_err)) { - error_propagate(errp, local_err); - return -1; - } - - /* cannot risk guest agent blocking itself on a write in this state */ - ga_set_frozen(ga_state); - - QTAILQ_FOREACH_REVERSE(mount, &mounts, next) { - /* To issue fsfreeze in the reverse order of mounts, check if the - * mount is listed in the list here */ - if (has_mountpoints) { - for (list =3D mountpoints; list; list =3D list->next) { - if (strcmp(list->value, mount->dirname) =3D=3D 0) { - break; - } - } - if (!list) { - continue; - } - } - - fd =3D qga_open_cloexec(mount->dirname, O_RDONLY, 0); - if (fd =3D=3D -1) { - error_setg_errno(errp, errno, "failed to open %s", mount->dirn= ame); - goto error; - } - - /* we try to cull filesystems we know won't work in advance, but o= ther - * filesystems may not implement fsfreeze for less obvious reasons. - * these will report EOPNOTSUPP. we simply ignore these when tally= ing - * the number of frozen filesystems. - * if a filesystem is mounted more than once (aka bind mount) a - * consecutive attempt to freeze an already frozen filesystem will - * return EBUSY. - * - * any other error means a failure to freeze a filesystem we - * expect to be freezable, so return an error in those cases - * and return system to thawed state. - */ - ret =3D ioctl(fd, FIFREEZE); - if (ret =3D=3D -1) { - if (errno !=3D EOPNOTSUPP && errno !=3D EBUSY) { - error_setg_errno(errp, errno, "failed to freeze %s", - mount->dirname); - close(fd); - goto error; - } - } else { - i++; - } - close(fd); - } - - free_fs_mount_list(&mounts); - /* We may not issue any FIFREEZE here. - * Just unset ga_state here and ready for the next call. - */ - if (i =3D=3D 0) { - ga_unset_frozen(ga_state); - } - return i; - -error: - free_fs_mount_list(&mounts); - qmp_guest_fsfreeze_thaw(NULL); - return 0; -} - -/* - * Walk list of frozen file systems in the guest, and thaw them. - */ -int64_t qmp_guest_fsfreeze_thaw(Error **errp) -{ - int ret; - FsMountList mounts; - FsMount *mount; - int fd, i =3D 0, logged; - Error *local_err =3D NULL; - - QTAILQ_INIT(&mounts); - if (!build_fs_mount_list(&mounts, &local_err)) { - error_propagate(errp, local_err); - return 0; - } - - QTAILQ_FOREACH(mount, &mounts, next) { - logged =3D false; - fd =3D qga_open_cloexec(mount->dirname, O_RDONLY, 0); - if (fd =3D=3D -1) { - continue; - } - /* we have no way of knowing whether a filesystem was actually unf= rozen - * as a result of a successful call to FITHAW, only that if an err= or - * was returned the filesystem was *not* unfrozen by that particul= ar - * call. - * - * since multiple preceding FIFREEZEs require multiple calls to FI= THAW - * to unfreeze, continuing issuing FITHAW until an error is return= ed, - * in which case either the filesystem is in an unfreezable state,= or, - * more likely, it was thawed previously (and remains so afterward= ). - * - * also, since the most recent successful call is the one that did - * the actual unfreeze, we can use this to provide an accurate cou= nt - * of the number of filesystems unfrozen by guest-fsfreeze-thaw, w= hich - * may * be useful for determining whether a filesystem was unfroz= en - * during the freeze/thaw phase by a process other than qemu-ga. - */ - do { - ret =3D ioctl(fd, FITHAW); - if (ret =3D=3D 0 && !logged) { - i++; - logged =3D true; - } - } while (ret =3D=3D 0); - close(fd); - } - - ga_unset_frozen(ga_state); - free_fs_mount_list(&mounts); - - execute_fsfreeze_hook(FSFREEZE_HOOK_THAW, errp); - - return i; -} - -static void guest_fsfreeze_cleanup(void) -{ - Error *err =3D NULL; - - if (ga_is_frozen(ga_state) =3D=3D GUEST_FSFREEZE_STATUS_FROZEN) { - qmp_guest_fsfreeze_thaw(&err); - if (err) { - slog("failed to clean up frozen filesystems: %s", - error_get_pretty(err)); - error_free(err); - } - } -} -#endif /* CONFIG_FSFREEZE */ - -#if defined(CONFIG_FSTRIM) -/* - * Walk list of mounted file systems in the guest, and trim them. - */ -GuestFilesystemTrimResponse * -qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp) -{ - GuestFilesystemTrimResponse *response; - GuestFilesystemTrimResult *result; - int ret =3D 0; - FsMountList mounts; - struct FsMount *mount; - int fd; - struct fstrim_range r; - - slog("guest-fstrim called"); - - QTAILQ_INIT(&mounts); - if (!build_fs_mount_list(&mounts, errp)) { - return NULL; - } - - response =3D g_malloc0(sizeof(*response)); - - QTAILQ_FOREACH(mount, &mounts, next) { - result =3D g_malloc0(sizeof(*result)); - result->path =3D g_strdup(mount->dirname); - - QAPI_LIST_PREPEND(response->paths, result); - - fd =3D qga_open_cloexec(mount->dirname, O_RDONLY, 0); - if (fd =3D=3D -1) { - result->error =3D g_strdup_printf("failed to open: %s", - strerror(errno)); - result->has_error =3D true; - continue; - } - - /* We try to cull filesystems we know won't work in advance, but o= ther - * filesystems may not implement fstrim for less obvious reasons. - * These will report EOPNOTSUPP; while in some other cases ENOTTY - * will be reported (e.g. CD-ROMs). - * Any other error means an unexpected error. - */ - r.start =3D 0; - r.len =3D -1; - r.minlen =3D has_minimum ? minimum : 0; - ret =3D ioctl(fd, FITRIM, &r); - if (ret =3D=3D -1) { - result->has_error =3D true; - if (errno =3D=3D ENOTTY || errno =3D=3D EOPNOTSUPP) { - result->error =3D g_strdup("trim not supported"); - } else { - result->error =3D g_strdup_printf("failed to trim: %s", - strerror(errno)); - } - close(fd); - continue; - } - - result->has_minimum =3D true; - result->minimum =3D r.minlen; - result->has_trimmed =3D true; - result->trimmed =3D r.len; - close(fd); - } - - free_fs_mount_list(&mounts); - return response; -} -#endif /* CONFIG_FSTRIM */ - - -#define LINUX_SYS_STATE_FILE "/sys/power/state" -#define SUSPEND_SUPPORTED 0 -#define SUSPEND_NOT_SUPPORTED 1 - -typedef enum { - SUSPEND_MODE_DISK =3D 0, - SUSPEND_MODE_RAM =3D 1, - SUSPEND_MODE_HYBRID =3D 2, -} SuspendMode; - -/* - * Executes a command in a child process using g_spawn_sync, - * returning an int >=3D 0 representing the exit status of the - * process. - * - * If the program wasn't found in path, returns -1. - * - * If a problem happened when creating the child process, - * returns -1 and errp is set. - */ -static int run_process_child(const char *command[], Error **errp) -{ - int exit_status, spawn_flag; - GError *g_err =3D NULL; - bool success; - - spawn_flag =3D G_SPAWN_SEARCH_PATH | G_SPAWN_STDOUT_TO_DEV_NULL | - G_SPAWN_STDERR_TO_DEV_NULL; - - success =3D g_spawn_sync(NULL, (char **)command, NULL, spawn_flag, - NULL, NULL, NULL, NULL, - &exit_status, &g_err); - - if (success) { - return WEXITSTATUS(exit_status); - } - - if (g_err && (g_err->code !=3D G_SPAWN_ERROR_NOENT)) { - error_setg(errp, "failed to create child process, error '%s'", - g_err->message); - } - - g_error_free(g_err); - return -1; -} - -static bool systemd_supports_mode(SuspendMode mode, Error **errp) -{ - const char *systemctl_args[3] =3D {"systemd-hibernate", "systemd-suspe= nd", - "systemd-hybrid-sleep"}; - const char *cmd[4] =3D {"systemctl", "status", systemctl_args[mode], N= ULL}; - int status; - - status =3D run_process_child(cmd, errp); - - /* - * systemctl status uses LSB return codes so we can expect - * status > 0 and be ok. To assert if the guest has support - * for the selected suspend mode, status should be < 4. 4 is - * the code for unknown service status, the return value when - * the service does not exist. A common value is status =3D 3 - * (program is not running). - */ - if (status > 0 && status < 4) { - return true; - } - - return false; -} - -static void systemd_suspend(SuspendMode mode, Error **errp) -{ - Error *local_err =3D NULL; - const char *systemctl_args[3] =3D {"hibernate", "suspend", "hybrid-sle= ep"}; - const char *cmd[3] =3D {"systemctl", systemctl_args[mode], NULL}; - int status; - - status =3D run_process_child(cmd, &local_err); - - if (status =3D=3D 0) { - return; - } - - if ((status =3D=3D -1) && !local_err) { - error_setg(errp, "the helper program 'systemctl %s' was not found", - systemctl_args[mode]); - return; - } - - if (local_err) { - error_propagate(errp, local_err); - } else { - error_setg(errp, "the helper program 'systemctl %s' returned an " - "unexpected exit status code (%d)", - systemctl_args[mode], status); - } -} - -static bool pmutils_supports_mode(SuspendMode mode, Error **errp) -{ - Error *local_err =3D NULL; - const char *pmutils_args[3] =3D {"--hibernate", "--suspend", - "--suspend-hybrid"}; - const char *cmd[3] =3D {"pm-is-supported", pmutils_args[mode], NULL}; - int status; - - status =3D run_process_child(cmd, &local_err); - - if (status =3D=3D SUSPEND_SUPPORTED) { - return true; - } - - if ((status =3D=3D -1) && !local_err) { - return false; - } - - if (local_err) { - error_propagate(errp, local_err); - } else { - error_setg(errp, - "the helper program '%s' returned an unexpected exit" - " status code (%d)", "pm-is-supported", status); - } - - return false; -} - -static void pmutils_suspend(SuspendMode mode, Error **errp) -{ - Error *local_err =3D NULL; - const char *pmutils_binaries[3] =3D {"pm-hibernate", "pm-suspend", - "pm-suspend-hybrid"}; - const char *cmd[2] =3D {pmutils_binaries[mode], NULL}; - int status; - - status =3D run_process_child(cmd, &local_err); - - if (status =3D=3D 0) { - return; - } - - if ((status =3D=3D -1) && !local_err) { - error_setg(errp, "the helper program '%s' was not found", - pmutils_binaries[mode]); - return; - } - - if (local_err) { - error_propagate(errp, local_err); - } else { - error_setg(errp, - "the helper program '%s' returned an unexpected exit" - " status code (%d)", pmutils_binaries[mode], status); - } -} - -static bool linux_sys_state_supports_mode(SuspendMode mode, Error **errp) -{ - const char *sysfile_strs[3] =3D {"disk", "mem", NULL}; - const char *sysfile_str =3D sysfile_strs[mode]; - char buf[32]; /* hopefully big enough */ - int fd; - ssize_t ret; - - if (!sysfile_str) { - error_setg(errp, "unknown guest suspend mode"); - return false; - } - - fd =3D open(LINUX_SYS_STATE_FILE, O_RDONLY); - if (fd < 0) { - return false; - } - - ret =3D read(fd, buf, sizeof(buf) - 1); - close(fd); - if (ret <=3D 0) { - return false; - } - buf[ret] =3D '\0'; - - if (strstr(buf, sysfile_str)) { - return true; - } - return false; -} - -static void linux_sys_state_suspend(SuspendMode mode, Error **errp) -{ - Error *local_err =3D NULL; - const char *sysfile_strs[3] =3D {"disk", "mem", NULL}; - const char *sysfile_str =3D sysfile_strs[mode]; - pid_t pid; - int status; - - if (!sysfile_str) { - error_setg(errp, "unknown guest suspend mode"); - return; - } - - pid =3D fork(); - if (!pid) { - /* child */ - int fd; - - setsid(); - reopen_fd_to_null(0); - reopen_fd_to_null(1); - reopen_fd_to_null(2); - - fd =3D open(LINUX_SYS_STATE_FILE, O_WRONLY); - if (fd < 0) { - _exit(EXIT_FAILURE); - } - - if (write(fd, sysfile_str, strlen(sysfile_str)) < 0) { - _exit(EXIT_FAILURE); - } - - _exit(EXIT_SUCCESS); - } else if (pid < 0) { - error_setg_errno(errp, errno, "failed to create child process"); - return; - } - - ga_wait_child(pid, &status, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - - if (WEXITSTATUS(status)) { - error_setg(errp, "child process has failed to suspend"); - } - -} - -static void guest_suspend(SuspendMode mode, Error **errp) -{ - Error *local_err =3D NULL; - bool mode_supported =3D false; - - if (systemd_supports_mode(mode, &local_err)) { - mode_supported =3D true; - systemd_suspend(mode, &local_err); - } - - if (!local_err) { - return; - } - - error_free(local_err); - local_err =3D NULL; - - if (pmutils_supports_mode(mode, &local_err)) { - mode_supported =3D true; - pmutils_suspend(mode, &local_err); - } - - if (!local_err) { - return; - } - - error_free(local_err); - local_err =3D NULL; - - if (linux_sys_state_supports_mode(mode, &local_err)) { - mode_supported =3D true; - linux_sys_state_suspend(mode, &local_err); - } - - if (!mode_supported) { - error_free(local_err); - error_setg(errp, - "the requested suspend mode is not supported by the gue= st"); - } else { - error_propagate(errp, local_err); - } -} - -void qmp_guest_suspend_disk(Error **errp) -{ - guest_suspend(SUSPEND_MODE_DISK, errp); -} - -void qmp_guest_suspend_ram(Error **errp) -{ - guest_suspend(SUSPEND_MODE_RAM, errp); -} - -void qmp_guest_suspend_hybrid(Error **errp) -{ - guest_suspend(SUSPEND_MODE_HYBRID, errp); -} - -/* Transfer online/offline status between @vcpu and the guest system. - * - * On input either @errp or *@errp must be NULL. - * - * In system-to-@vcpu direction, the following @vcpu fields are accessed: - * - R: vcpu->logical_id - * - W: vcpu->online - * - W: vcpu->can_offline - * - * In @vcpu-to-system direction, the following @vcpu fields are accessed: - * - R: vcpu->logical_id - * - R: vcpu->online - * - * Written members remain unmodified on error. - */ -static void transfer_vcpu(GuestLogicalProcessor *vcpu, bool sys2vcpu, - char *dirpath, Error **errp) -{ - int fd; - int res; - int dirfd; - static const char fn[] =3D "online"; - - dirfd =3D open(dirpath, O_RDONLY | O_DIRECTORY); - if (dirfd =3D=3D -1) { - error_setg_errno(errp, errno, "open(\"%s\")", dirpath); - return; - } - - fd =3D openat(dirfd, fn, sys2vcpu ? O_RDONLY : O_RDWR); - if (fd =3D=3D -1) { - if (errno !=3D ENOENT) { - error_setg_errno(errp, errno, "open(\"%s/%s\")", dirpath, fn); - } else if (sys2vcpu) { - vcpu->online =3D true; - vcpu->can_offline =3D false; - } else if (!vcpu->online) { - error_setg(errp, "logical processor #%" PRId64 " can't be " - "offlined", vcpu->logical_id); - } /* otherwise pretend successful re-onlining */ - } else { - unsigned char status; - - res =3D pread(fd, &status, 1, 0); - if (res =3D=3D -1) { - error_setg_errno(errp, errno, "pread(\"%s/%s\")", dirpath, fn); - } else if (res =3D=3D 0) { - error_setg(errp, "pread(\"%s/%s\"): unexpected EOF", dirpath, - fn); - } else if (sys2vcpu) { - vcpu->online =3D (status !=3D '0'); - vcpu->can_offline =3D true; - } else if (vcpu->online !=3D (status !=3D '0')) { - status =3D '0' + vcpu->online; - if (pwrite(fd, &status, 1, 0) =3D=3D -1) { - error_setg_errno(errp, errno, "pwrite(\"%s/%s\")", dirpath, - fn); - } - } /* otherwise pretend successful re-(on|off)-lining */ - - res =3D close(fd); - g_assert(res =3D=3D 0); - } - - res =3D close(dirfd); - g_assert(res =3D=3D 0); -} - -GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp) -{ - GuestLogicalProcessorList *head, **tail; - const char *cpu_dir =3D "/sys/devices/system/cpu"; - const gchar *line; - g_autoptr(GDir) cpu_gdir =3D NULL; - Error *local_err =3D NULL; - - head =3D NULL; - tail =3D &head; - cpu_gdir =3D g_dir_open(cpu_dir, 0, NULL); - - if (cpu_gdir =3D=3D NULL) { - error_setg_errno(errp, errno, "failed to list entries: %s", cpu_di= r); - return NULL; - } - - while (local_err =3D=3D NULL && (line =3D g_dir_read_name(cpu_gdir)) != =3D NULL) { - GuestLogicalProcessor *vcpu; - int64_t id; - if (sscanf(line, "cpu%" PRId64, &id)) { - g_autofree char *path =3D g_strdup_printf("/sys/devices/system= /cpu/" - "cpu%" PRId64 "/", id); - vcpu =3D g_malloc0(sizeof *vcpu); - vcpu->logical_id =3D id; - vcpu->has_can_offline =3D true; /* lolspeak ftw */ - transfer_vcpu(vcpu, true, path, &local_err); - QAPI_LIST_APPEND(tail, vcpu); - } - } - - if (local_err =3D=3D NULL) { - /* there's no guest with zero VCPUs */ - g_assert(head !=3D NULL); - return head; - } - - qapi_free_GuestLogicalProcessorList(head); - error_propagate(errp, local_err); - return NULL; -} - -int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp) -{ - int64_t processed; - Error *local_err =3D NULL; - - processed =3D 0; - while (vcpus !=3D NULL) { - char *path =3D g_strdup_printf("/sys/devices/system/cpu/cpu%" PRId= 64 "/", - vcpus->value->logical_id); - - transfer_vcpu(vcpus->value, false, path, &local_err); - g_free(path); - if (local_err !=3D NULL) { - break; - } - ++processed; - vcpus =3D vcpus->next; - } - - if (local_err !=3D NULL) { - if (processed =3D=3D 0) { - error_propagate(errp, local_err); - } else { - error_free(local_err); - } - } - - return processed; -} - -void qmp_guest_set_user_password(const char *username, - const char *password, - bool crypted, - Error **errp) -{ - Error *local_err =3D NULL; - char *passwd_path =3D NULL; - pid_t pid; - int status; - int datafd[2] =3D { -1, -1 }; - char *rawpasswddata =3D NULL; - size_t rawpasswdlen; - char *chpasswddata =3D NULL; - size_t chpasswdlen; - - rawpasswddata =3D (char *)qbase64_decode(password, -1, &rawpasswdlen, = errp); - if (!rawpasswddata) { - return; - } - rawpasswddata =3D g_renew(char, rawpasswddata, rawpasswdlen + 1); - rawpasswddata[rawpasswdlen] =3D '\0'; - - if (strchr(rawpasswddata, '\n')) { - error_setg(errp, "forbidden characters in raw password"); - goto out; - } - - if (strchr(username, '\n') || - strchr(username, ':')) { - error_setg(errp, "forbidden characters in username"); - goto out; - } - - chpasswddata =3D g_strdup_printf("%s:%s\n", username, rawpasswddata); - chpasswdlen =3D strlen(chpasswddata); - - passwd_path =3D g_find_program_in_path("chpasswd"); - - if (!passwd_path) { - error_setg(errp, "cannot find 'passwd' program in PATH"); - goto out; - } - - if (!g_unix_open_pipe(datafd, FD_CLOEXEC, NULL)) { - error_setg(errp, "cannot create pipe FDs"); - goto out; - } - - pid =3D fork(); - if (pid =3D=3D 0) { - close(datafd[1]); - /* child */ - setsid(); - dup2(datafd[0], 0); - reopen_fd_to_null(1); - reopen_fd_to_null(2); - - if (crypted) { - execl(passwd_path, "chpasswd", "-e", NULL); - } else { - execl(passwd_path, "chpasswd", NULL); - } - _exit(EXIT_FAILURE); - } else if (pid < 0) { - error_setg_errno(errp, errno, "failed to create child process"); - goto out; - } - close(datafd[0]); - datafd[0] =3D -1; - - if (qemu_write_full(datafd[1], chpasswddata, chpasswdlen) !=3D chpassw= dlen) { - error_setg_errno(errp, errno, "cannot write new account password"); - goto out; - } - close(datafd[1]); - datafd[1] =3D -1; - - ga_wait_child(pid, &status, &local_err); - if (local_err) { - error_propagate(errp, local_err); - goto out; - } - - if (!WIFEXITED(status)) { - error_setg(errp, "child process has terminated abnormally"); - goto out; - } - - if (WEXITSTATUS(status)) { - error_setg(errp, "child process has failed to set user password"); - goto out; - } - -out: - g_free(chpasswddata); - g_free(rawpasswddata); - g_free(passwd_path); - if (datafd[0] !=3D -1) { - close(datafd[0]); - } - if (datafd[1] !=3D -1) { - close(datafd[1]); - } -} - -static void ga_read_sysfs_file(int dirfd, const char *pathname, char *buf, - int size, Error **errp) -{ - int fd; - int res; - - errno =3D 0; - fd =3D openat(dirfd, pathname, O_RDONLY); - if (fd =3D=3D -1) { - error_setg_errno(errp, errno, "open sysfs file \"%s\"", pathname); - return; - } - - res =3D pread(fd, buf, size, 0); - if (res =3D=3D -1) { - error_setg_errno(errp, errno, "pread sysfs file \"%s\"", pathname); - } else if (res =3D=3D 0) { - error_setg(errp, "pread sysfs file \"%s\": unexpected EOF", pathna= me); - } - close(fd); -} - -static void ga_write_sysfs_file(int dirfd, const char *pathname, - const char *buf, int size, Error **errp) -{ - int fd; - - errno =3D 0; - fd =3D openat(dirfd, pathname, O_WRONLY); - if (fd =3D=3D -1) { - error_setg_errno(errp, errno, "open sysfs file \"%s\"", pathname); - return; - } - - if (pwrite(fd, buf, size, 0) =3D=3D -1) { - error_setg_errno(errp, errno, "pwrite sysfs file \"%s\"", pathname= ); - } - - close(fd); -} - -/* Transfer online/offline status between @mem_blk and the guest system. - * - * On input either @errp or *@errp must be NULL. - * - * In system-to-@mem_blk direction, the following @mem_blk fields are acce= ssed: - * - R: mem_blk->phys_index - * - W: mem_blk->online - * - W: mem_blk->can_offline - * - * In @mem_blk-to-system direction, the following @mem_blk fields are acce= ssed: - * - R: mem_blk->phys_index - * - R: mem_blk->online - *- R: mem_blk->can_offline - * Written members remain unmodified on error. - */ -static void transfer_memory_block(GuestMemoryBlock *mem_blk, bool sys2memb= lk, - GuestMemoryBlockResponse *result, - Error **errp) -{ - char *dirpath; - int dirfd; - char *status; - Error *local_err =3D NULL; - - if (!sys2memblk) { - DIR *dp; - - if (!result) { - error_setg(errp, "Internal error, 'result' should not be NULL"= ); - return; - } - errno =3D 0; - dp =3D opendir("/sys/devices/system/memory/"); - /* if there is no 'memory' directory in sysfs, - * we think this VM does not support online/offline memory block, - * any other solution? - */ - if (!dp) { - if (errno =3D=3D ENOENT) { - result->response =3D - GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_NOT_SUPPORT= ED; - } - goto out1; - } - closedir(dp); - } - - dirpath =3D g_strdup_printf("/sys/devices/system/memory/memory%" PRId6= 4 "/", - mem_blk->phys_index); - dirfd =3D open(dirpath, O_RDONLY | O_DIRECTORY); - if (dirfd =3D=3D -1) { - if (sys2memblk) { - error_setg_errno(errp, errno, "open(\"%s\")", dirpath); - } else { - if (errno =3D=3D ENOENT) { - result->response =3D GUEST_MEMORY_BLOCK_RESPONSE_TYPE_NOT_= FOUND; - } else { - result->response =3D - GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_FAILED; - } - } - g_free(dirpath); - goto out1; - } - g_free(dirpath); - - status =3D g_malloc0(10); - ga_read_sysfs_file(dirfd, "state", status, 10, &local_err); - if (local_err) { - /* treat with sysfs file that not exist in old kernel */ - if (errno =3D=3D ENOENT) { - error_free(local_err); - if (sys2memblk) { - mem_blk->online =3D true; - mem_blk->can_offline =3D false; - } else if (!mem_blk->online) { - result->response =3D - GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_NOT_SUPPORT= ED; - } - } else { - if (sys2memblk) { - error_propagate(errp, local_err); - } else { - error_free(local_err); - result->response =3D - GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_FAILED; - } - } - goto out2; - } - - if (sys2memblk) { - char removable =3D '0'; - - mem_blk->online =3D (strncmp(status, "online", 6) =3D=3D 0); - - ga_read_sysfs_file(dirfd, "removable", &removable, 1, &local_err); - if (local_err) { - /* if no 'removable' file, it doesn't support offline mem blk = */ - if (errno =3D=3D ENOENT) { - error_free(local_err); - mem_blk->can_offline =3D false; - } else { - error_propagate(errp, local_err); - } - } else { - mem_blk->can_offline =3D (removable !=3D '0'); - } - } else { - if (mem_blk->online !=3D (strncmp(status, "online", 6) =3D=3D 0)) { - const char *new_state =3D mem_blk->online ? "online" : "offlin= e"; - - ga_write_sysfs_file(dirfd, "state", new_state, strlen(new_stat= e), - &local_err); - if (local_err) { - error_free(local_err); - result->response =3D - GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_FAILED; - goto out2; - } - - result->response =3D GUEST_MEMORY_BLOCK_RESPONSE_TYPE_SUCCESS; - result->has_error_code =3D false; - } /* otherwise pretend successful re-(on|off)-lining */ - } - g_free(status); - close(dirfd); - return; - -out2: - g_free(status); - close(dirfd); -out1: - if (!sys2memblk) { - result->has_error_code =3D true; - result->error_code =3D errno; - } -} - -GuestMemoryBlockList *qmp_guest_get_memory_blocks(Error **errp) -{ - GuestMemoryBlockList *head, **tail; - Error *local_err =3D NULL; - struct dirent *de; - DIR *dp; - - head =3D NULL; - tail =3D &head; - - dp =3D opendir("/sys/devices/system/memory/"); - if (!dp) { - /* it's ok if this happens to be a system that doesn't expose - * memory blocks via sysfs, but otherwise we should report - * an error - */ - if (errno !=3D ENOENT) { - error_setg_errno(errp, errno, "Can't open directory" - "\"/sys/devices/system/memory/\""); - } - return NULL; - } - - /* Note: the phys_index of memory block may be discontinuous, - * this is because a memblk is the unit of the Sparse Memory design, w= hich - * allows discontinuous memory ranges (ex. NUMA), so here we should - * traverse the memory block directory. - */ - while ((de =3D readdir(dp)) !=3D NULL) { - GuestMemoryBlock *mem_blk; - - if ((strncmp(de->d_name, "memory", 6) !=3D 0) || - !(de->d_type & DT_DIR)) { - continue; - } - - mem_blk =3D g_malloc0(sizeof *mem_blk); - /* The d_name is "memoryXXX", phys_index is block id, same as XXX= */ - mem_blk->phys_index =3D strtoul(&de->d_name[6], NULL, 10); - mem_blk->has_can_offline =3D true; /* lolspeak ftw */ - transfer_memory_block(mem_blk, true, NULL, &local_err); - if (local_err) { - break; - } - - QAPI_LIST_APPEND(tail, mem_blk); - } - - closedir(dp); - if (local_err =3D=3D NULL) { - /* there's no guest with zero memory blocks */ - if (head =3D=3D NULL) { - error_setg(errp, "guest reported zero memory blocks!"); - } - return head; - } - - qapi_free_GuestMemoryBlockList(head); - error_propagate(errp, local_err); - return NULL; -} - -GuestMemoryBlockResponseList * -qmp_guest_set_memory_blocks(GuestMemoryBlockList *mem_blks, Error **errp) -{ - GuestMemoryBlockResponseList *head, **tail; - Error *local_err =3D NULL; - - head =3D NULL; - tail =3D &head; - - while (mem_blks !=3D NULL) { - GuestMemoryBlockResponse *result; - GuestMemoryBlock *current_mem_blk =3D mem_blks->value; - - result =3D g_malloc0(sizeof(*result)); - result->phys_index =3D current_mem_blk->phys_index; - transfer_memory_block(current_mem_blk, false, result, &local_err); - if (local_err) { /* should never happen */ - goto err; - } - - QAPI_LIST_APPEND(tail, result); - mem_blks =3D mem_blks->next; - } - - return head; -err: - qapi_free_GuestMemoryBlockResponseList(head); - error_propagate(errp, local_err); - return NULL; -} - -GuestMemoryBlockInfo *qmp_guest_get_memory_block_info(Error **errp) -{ - Error *local_err =3D NULL; - char *dirpath; - int dirfd; - char *buf; - GuestMemoryBlockInfo *info; - - dirpath =3D g_strdup_printf("/sys/devices/system/memory/"); - dirfd =3D open(dirpath, O_RDONLY | O_DIRECTORY); - if (dirfd =3D=3D -1) { - error_setg_errno(errp, errno, "open(\"%s\")", dirpath); - g_free(dirpath); - return NULL; - } - g_free(dirpath); - - buf =3D g_malloc0(20); - ga_read_sysfs_file(dirfd, "block_size_bytes", buf, 20, &local_err); - close(dirfd); - if (local_err) { - g_free(buf); - error_propagate(errp, local_err); - return NULL; - } - - info =3D g_new0(GuestMemoryBlockInfo, 1); - info->size =3D strtol(buf, NULL, 16); /* the unit is bytes */ - - g_free(buf); - - return info; -} - -#define MAX_NAME_LEN 128 -static GuestDiskStatsInfoList *guest_get_diskstats(Error **errp) -{ -#ifdef CONFIG_LINUX - GuestDiskStatsInfoList *head =3D NULL, **tail =3D &head; - const char *diskstats =3D "/proc/diskstats"; - FILE *fp; - size_t n; - char *line =3D NULL; - - fp =3D fopen(diskstats, "r"); - if (fp =3D=3D NULL) { - error_setg_errno(errp, errno, "open(\"%s\")", diskstats); - return NULL; - } - - while (getline(&line, &n, fp) !=3D -1) { - g_autofree GuestDiskStatsInfo *diskstatinfo =3D NULL; - g_autofree GuestDiskStats *diskstat =3D NULL; - char dev_name[MAX_NAME_LEN]; - unsigned int ios_pgr, tot_ticks, rq_ticks, wr_ticks, dc_ticks, fl_= ticks; - unsigned long rd_ios, rd_merges_or_rd_sec, rd_ticks_or_wr_sec, wr_= ios; - unsigned long wr_merges, rd_sec_or_wr_ios, wr_sec; - unsigned long dc_ios, dc_merges, dc_sec, fl_ios; - unsigned int major, minor; - int i; - - i =3D sscanf(line, "%u %u %s %lu %lu %lu" - "%lu %lu %lu %lu %u %u %u %u" - "%lu %lu %lu %u %lu %u", - &major, &minor, dev_name, - &rd_ios, &rd_merges_or_rd_sec, &rd_sec_or_wr_ios, - &rd_ticks_or_wr_sec, &wr_ios, &wr_merges, &wr_sec, - &wr_ticks, &ios_pgr, &tot_ticks, &rq_ticks, - &dc_ios, &dc_merges, &dc_sec, &dc_ticks, - &fl_ios, &fl_ticks); - - if (i < 7) { - continue; - } - - diskstatinfo =3D g_new0(GuestDiskStatsInfo, 1); - diskstatinfo->name =3D g_strdup(dev_name); - diskstatinfo->major =3D major; - diskstatinfo->minor =3D minor; - - diskstat =3D g_new0(GuestDiskStats, 1); - if (i =3D=3D 7) { - diskstat->has_read_ios =3D true; - diskstat->read_ios =3D rd_ios; - diskstat->has_read_sectors =3D true; - diskstat->read_sectors =3D rd_merges_or_rd_sec; - diskstat->has_write_ios =3D true; - diskstat->write_ios =3D rd_sec_or_wr_ios; - diskstat->has_write_sectors =3D true; - diskstat->write_sectors =3D rd_ticks_or_wr_sec; - } - if (i >=3D 14) { - diskstat->has_read_ios =3D true; - diskstat->read_ios =3D rd_ios; - diskstat->has_read_sectors =3D true; - diskstat->read_sectors =3D rd_sec_or_wr_ios; - diskstat->has_read_merges =3D true; - diskstat->read_merges =3D rd_merges_or_rd_sec; - diskstat->has_read_ticks =3D true; - diskstat->read_ticks =3D rd_ticks_or_wr_sec; - diskstat->has_write_ios =3D true; - diskstat->write_ios =3D wr_ios; - diskstat->has_write_sectors =3D true; - diskstat->write_sectors =3D wr_sec; - diskstat->has_write_merges =3D true; - diskstat->write_merges =3D wr_merges; - diskstat->has_write_ticks =3D true; - diskstat->write_ticks =3D wr_ticks; - diskstat->has_ios_pgr =3D true; - diskstat->ios_pgr =3D ios_pgr; - diskstat->has_total_ticks =3D true; - diskstat->total_ticks =3D tot_ticks; - diskstat->has_weight_ticks =3D true; - diskstat->weight_ticks =3D rq_ticks; - } - if (i >=3D 18) { - diskstat->has_discard_ios =3D true; - diskstat->discard_ios =3D dc_ios; - diskstat->has_discard_merges =3D true; - diskstat->discard_merges =3D dc_merges; - diskstat->has_discard_sectors =3D true; - diskstat->discard_sectors =3D dc_sec; - diskstat->has_discard_ticks =3D true; - diskstat->discard_ticks =3D dc_ticks; - } - if (i >=3D 20) { - diskstat->has_flush_ios =3D true; - diskstat->flush_ios =3D fl_ios; - diskstat->has_flush_ticks =3D true; - diskstat->flush_ticks =3D fl_ticks; - } - - diskstatinfo->stats =3D g_steal_pointer(&diskstat); - QAPI_LIST_APPEND(tail, diskstatinfo); - diskstatinfo =3D NULL; - } - free(line); - fclose(fp); - return head; -#else - g_debug("disk stats reporting available only for Linux"); - return NULL; -#endif -} - -GuestDiskStatsInfoList *qmp_guest_get_diskstats(Error **errp) -{ - return guest_get_diskstats(errp); -} - -GuestCpuStatsList *qmp_guest_get_cpustats(Error **errp) -{ - GuestCpuStatsList *head =3D NULL, **tail =3D &head; - const char *cpustats =3D "/proc/stat"; - int clk_tck =3D sysconf(_SC_CLK_TCK); - FILE *fp; - size_t n; - char *line =3D NULL; - - fp =3D fopen(cpustats, "r"); - if (fp =3D=3D NULL) { - error_setg_errno(errp, errno, "open(\"%s\")", cpustats); - return NULL; - } - - while (getline(&line, &n, fp) !=3D -1) { - GuestCpuStats *cpustat =3D NULL; - GuestLinuxCpuStats *linuxcpustat; - int i; - unsigned long user, system, idle, iowait, irq, softirq, steal, gue= st; - unsigned long nice, guest_nice; - char name[64]; - - i =3D sscanf(line, "%s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu", - name, &user, &nice, &system, &idle, &iowait, &irq, &sof= tirq, - &steal, &guest, &guest_nice); - - /* drop "cpu 1 2 3 ...", get "cpuX 1 2 3 ..." only */ - if ((i =3D=3D EOF) || strncmp(name, "cpu", 3) || (name[3] =3D=3D '= \0')) { - continue; - } - - if (i < 5) { - slog("Parsing cpu stat from %s failed, see \"man proc\"", cpus= tats); - break; - } - - cpustat =3D g_new0(GuestCpuStats, 1); - cpustat->type =3D GUEST_CPU_STATS_TYPE_LINUX; - - linuxcpustat =3D &cpustat->u.q_linux; - linuxcpustat->cpu =3D atoi(&name[3]); - linuxcpustat->user =3D user * 1000 / clk_tck; - linuxcpustat->nice =3D nice * 1000 / clk_tck; - linuxcpustat->system =3D system * 1000 / clk_tck; - linuxcpustat->idle =3D idle * 1000 / clk_tck; - - if (i > 5) { - linuxcpustat->has_iowait =3D true; - linuxcpustat->iowait =3D iowait * 1000 / clk_tck; - } - - if (i > 6) { - linuxcpustat->has_irq =3D true; - linuxcpustat->irq =3D irq * 1000 / clk_tck; - linuxcpustat->has_softirq =3D true; - linuxcpustat->softirq =3D softirq * 1000 / clk_tck; - } - - if (i > 8) { - linuxcpustat->has_steal =3D true; - linuxcpustat->steal =3D steal * 1000 / clk_tck; - } - - if (i > 9) { - linuxcpustat->has_guest =3D true; - linuxcpustat->guest =3D guest * 1000 / clk_tck; - } - - if (i > 10) { - linuxcpustat->has_guest =3D true; - linuxcpustat->guest =3D guest * 1000 / clk_tck; - linuxcpustat->has_guestnice =3D true; - linuxcpustat->guestnice =3D guest_nice * 1000 / clk_tck; - } - - QAPI_LIST_APPEND(tail, cpustat); - } - - free(line); - fclose(fp); - return head; -} - -#else /* defined(__linux__) */ +#if !defined(__linux__) =20 void qmp_guest_suspend_disk(Error **errp) { diff --git a/qga/meson.build b/qga/meson.build index 65c1e93846..409f49a000 100644 --- a/qga/meson.build +++ b/qga/meson.build @@ -72,6 +72,9 @@ qga_ss.add(when: 'CONFIG_POSIX', if_true: files( 'commands-posix.c', 'commands-posix-ssh.c', )) +qga_ss.add(when: 'CONFIG_LINUX', if_true: files( + 'commands-linux.c', +)) qga_ss.add(when: 'CONFIG_WIN32', if_true: files( 'channel-win32.c', 'commands-win32.c', --=20 2.34.1 From nobody Fri Mar 29 08:16:24 2024 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; arc=pass (i=1 dmarc=pass fromdomain=virtuozzo.com); dmarc=pass(p=quarantine dis=none) header.from=virtuozzo.com ARC-Seal: i=2; a=rsa-sha256; t=1663856993; cv=pass; d=zohomail.com; s=zohoarc; b=LDqGNdDXNhBR9zGTFYwyQSdW7fJJFNyaGxh/kA30Y66Hnb+g+7B+D0xplrdfxyVmcyO1wNJfTz1kA+BQW0GHb2AyzslT4qf+pDh/r7U7tKh1NxkR+o+mZ6Q+tvVXHNh4mniBsWjGVHwBATqhf5lr8KwmpxGGuF93KET+NW4tr10= ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1663856993; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=eFrv1ufMIEuTjrrp9MSpxURObbKKCc+IpHyH3IZgoEc=; b=eiYHGIo8SrDCuUC0ZGcVWHZkv72EJJY7mnkf4J9ZFwF91QF0aUmOWlPC20XN6cAA+CjkbWzmbntmXgRNVN0JYyx4NN7wHS34/mcVwrySIktwzCGrqraft1VhaS46k1A8DrBPOif+oX2FKt5dCjFpmZsRTltc9iJtFXJyEnCmnMU= ARC-Authentication-Results: i=2; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; arc=pass (i=1 dmarc=pass fromdomain=virtuozzo.com); dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1663856993811325.08517525351226; Thu, 22 Sep 2022 07:29:53 -0700 (PDT) Received: from localhost ([::1]:60568 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1obND7-00051h-1Y for importer@patchew.org; Thu, 22 Sep 2022 10:29:53 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:59920) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1obMCi-0003pJ-Ok for qemu-devel@nongnu.org; Thu, 22 Sep 2022 09:25:27 -0400 Received: from mail-db3eur04on0703.outbound.protection.outlook.com ([2a01:111:f400:fe0c::703]:39940 helo=EUR04-DB3-obe.outbound.protection.outlook.com) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1obMCg-00082M-FJ for qemu-devel@nongnu.org; Thu, 22 Sep 2022 09:25:24 -0400 Received: from AS8PR08MB7095.eurprd08.prod.outlook.com (2603:10a6:20b:402::11) by PAXPR08MB6480.eurprd08.prod.outlook.com (2603:10a6:102:155::10) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.5654.16; Thu, 22 Sep 2022 13:20:13 +0000 Received: from AS8PR08MB7095.eurprd08.prod.outlook.com ([fe80::5174:25c7:6df8:741b]) by AS8PR08MB7095.eurprd08.prod.outlook.com ([fe80::5174:25c7:6df8:741b%8]) with mapi id 15.20.5654.016; Thu, 22 Sep 2022 13:20:13 +0000 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=ORrYfGohE1TTYfSv88zkOxHCk8q/w/KR9fa3lVamZohp5Bprq0acTDCKMpqN3faCPWhk+jLNbBls46xQHOcpmE4bQ79q8GRZ3Ohl28PECt2V1l13eQOCV5UCF4qPGJM0LWbmRjXl6bcgRgkC9qQP+ZRXmNYM/dNSlpvBtygp1ruNdiEqTbgIrgiCEZ2IRIjX2CS3Dv/opBjY4MgVHumSyyTh3nDham7wcmS5NSJ/iulvoH2Rj1A7AurQ8E99dQyU03PmSmYvsSXQjTrVRabsiSvMNM94ZSGGH8R/NjAUgyRHm3unO/HRIH1Ks2iOd/xyTFi+5IFQNYA2UvRDpu4Dwg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=eFrv1ufMIEuTjrrp9MSpxURObbKKCc+IpHyH3IZgoEc=; b=YfDKzriKk6Cl3T2qNTq8oCsu+b6LF4xFfTzKYD48vAfvtGM6Px859X3d8OvWOuitv54Wm3dUIGVNImymQIQIySLY9yP1z7qWx1y+LMqUMHeTPI+Qti2JpgefceQsLstloBUaNAgMXxCSJuF1J2bSTXDBsE87tEVdlHPqReRdyxXqRZZjk1SxNa+63LxHiViygqLUqcTRTbIomDCrnXylTeThPIpCEMYUg4imqUe2hlHP+q1HZPY7Av4tvciv1c8q5o/7lOfpzzYHIQJPm8BNh4mEYUn7GPypI5mTHeWM+n1+4zhL5Qowmx0Kei9lhRoFx5UCphgg2+G7r6XlCd/x4g== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=virtuozzo.com; dmarc=pass action=none header.from=virtuozzo.com; dkim=pass header.d=virtuozzo.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=virtuozzo.com; s=selector2; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=eFrv1ufMIEuTjrrp9MSpxURObbKKCc+IpHyH3IZgoEc=; b=TyNVFUIRTv4Hs94k6IhEyJM2GSx3t5S58XfU+2JukmXtJLgbYIRpPmlWqNFYc0+vVkO3TpU0aKvCpb0+10GsujxDa43FssqVtYdS2AkhSHZ0d44YfKeioRXGyiEdiZTlkHnXZVuiM9r+dXmSTgp+IqPrpYMEyjgsL/+LY//rYB1xdcjIofSj8lTbLcu7UY6kLI31M/RnwegsGQTvxmYcQ8mO8/YNP6Jiuq/welutpOuGCSIHOrWFMJxklWEW2DGWNofj6aQFP4f9JXnuv/0iOsMeODje/akPkv59npfVpcKpHMR8NM4i0Prvjiy3ufDwxpGcHiQjehwTOpj1Jwt5eg== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=virtuozzo.com; From: Alexander Ivanov To: qemu-devel@nongnu.org Cc: den@virtuozzo.com, michael.roth@amd.com, kkostiuk@redhat.com Subject: [PATCH 2/5] qga: Add initial FreeBSD support Date: Thu, 22 Sep 2022 15:19:58 +0200 Message-Id: <20220922132001.940334-3-alexander.ivanov@virtuozzo.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220922132001.940334-1-alexander.ivanov@virtuozzo.com> References: <20220922132001.940334-1-alexander.ivanov@virtuozzo.com> Content-Transfer-Encoding: quoted-printable X-ClientProxiedBy: FR0P281CA0083.DEUP281.PROD.OUTLOOK.COM (2603:10a6:d10:1e::18) To AS8PR08MB7095.eurprd08.prod.outlook.com (2603:10a6:20b:402::11) MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: AS8PR08MB7095:EE_|PAXPR08MB6480:EE_ X-MS-Office365-Filtering-Correlation-Id: edd79205-a00b-4666-4ae2-08da9c9d2e72 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: DGLHHiBuAJSUEM6adHfNOsb84RP+TlrHJ35Kju/PkJRKVVGKyt1KXRXfh2MA7yJldU5q+4McYRb2erdUBnl1iw3snEwrr8t1MMSIOD5uzKznRcCu59hExvWW2encr/xaLyDPLDFBUdyt2ADsODwGjxFs38akOmWr6CQCGG6UixpfsZLq7zlcGUPq5h4KFnQa9qAD+Dewxm0D4EkKTprTAF2mw1H1JDmgizCd+kSFr1vfjhMJsgHT4+eIBunpkzQwi96mAt0v8l0PC4vhyht4FKvJGss1/c4kVFBRz7giA0bdRJDoqkIR/Uc7D7R387xG0tDvCVXiLqJ0zIXGXMP2Drfw/I7Our/xNbc5TbxJcQv+gnVhi8438GmGZAne9LryhlVfNR6GJihn2FB0msWjzyDE8epqKrmly43VVALnN/Y83QyeH/QXhNbMqTMusqLjKG3je1mWid0FHH6ZCrIQ0XWRbaqrSdgeef57zC2JTUz/EUtAVUxMsR8YTwR2KRtr8AEXopdmeYd+oZIzs+Ft9x+578s1gkHpq+zi6u0Ze2bDMcv/w5EvYf8tXBqt22o0td+UPX33+7m+nYYCn9JsOR+zwAMYS/p+96qu5SAs5irvyUoitUD5qNo0AfTmLba4H66Sg+kr5Ws2APwLTKDv8klqm+JR6kKmFC8svQUiz/HdqQ8eX5vLr4vi1ZC0iJT3o0q2aDOSFrRsTJEGpX2r5lPBCYfutmYaIMxrUwYmKJQtGvpkZ7an3Jjf4q540na4 X-Forefront-Antispam-Report: CIP:255.255.255.255; CTRY:; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:AS8PR08MB7095.eurprd08.prod.outlook.com; PTR:; CAT:NONE; SFS:(13230022)(4636009)(346002)(366004)(376002)(136003)(396003)(39850400004)(451199015)(41300700001)(6486002)(478600001)(6666004)(52116002)(36756003)(66476007)(66556008)(316002)(186003)(66946007)(83380400001)(1076003)(5660300002)(2616005)(8936002)(26005)(6506007)(6916009)(86362001)(8676002)(44832011)(4326008)(2906002)(6512007)(38350700002)(38100700002); DIR:OUT; SFP:1102; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?us-ascii?Q?+8bExo5Pkr8dJVisy6Aq0zYKLs7bW7ucu1M5zdvBkSFwCkFx9k8zHdGiD+jX?= =?us-ascii?Q?J4sL5/6I4ZZAzpdAPACrQtlSCB8vzJ7V2fT4gjBjBydyM7xIjpO0ZjxectoP?= =?us-ascii?Q?CvfV3KPvqaPPADdvj3eAcO+Kv/x3A20pY4FXO1E7CVNja9kzEMpPCUxuhVV8?= =?us-ascii?Q?mUW+s4LyN5J6D+yBz6L8xX6qoJTAaL0ScTunBr4iWJ7OsS5TDK/dzu1IZHbN?= =?us-ascii?Q?HcZUySk4iJv4uJi2v897WrtX606HSZo0sq8CuPSaGvk8hA+WJPSk2awTeaMI?= =?us-ascii?Q?+YHGr+aYLDfT2oSySF1PdPwIXsZc/afkbZvnf6IBnJkPyeE12N4OGvxBl0DB?= =?us-ascii?Q?DmfFqdb24+0YLHXlwbPcA4PW8qorJ1WP5yMhBNEKbaj/kIBGJP1TaV1pwt+C?= =?us-ascii?Q?UPwLHRilVCMHzxOylT8RINNPVBMpXULYRBWfuSvFhMHj3vw7DIUWF5gnrq+O?= =?us-ascii?Q?5M6lDxjbNVGVcijk1dybPXMUDGJtHdLLXSOXU4pdtsdBipnNy6zswJSIqoBV?= =?us-ascii?Q?rT2krQ2IivlecPq6Ru3U4TkuNmlaDTN5e1qBMN7RuGQQP7WRGXnogGQZFSLV?= =?us-ascii?Q?6BDMbg4Vg96S/dPJpLaqlNFdYODb6V96noTt5jzlaXtFz4Se8wOhffbkdnVq?= =?us-ascii?Q?v9M8A9HEp6gug2W+L+tyWWB9MV55c55CdJqhqg10+hh6y/bOXDFqrFPCuQgc?= =?us-ascii?Q?weyHmICF7odapz2VDMLN7l7QvCd1Pitp39DL5EalXau7Hsa8qDsmLlJbjfAM?= =?us-ascii?Q?wN2LqjcC8P0NCuc29Dbh17udwzBxJdXpRfIGSd+VdvYIQzyvZcNFlSW2p1Yt?= =?us-ascii?Q?OQCBXCfGtReBiRjKPtyA1geAGAdwgF8AZj6yVqRhI3b0naV72HVQMmJjC18H?= =?us-ascii?Q?b/MSSvnJ9OqPSSwR6UIKg/WD42tEu3jHoy4m/v1PkYzzEFgYnpjonbcwTe10?= =?us-ascii?Q?EyN0yoDL3ljqL759ob5kHPreaStIfiUWZBlYhkKR1Ezcyzun1A+Ul5iv/Ora?= =?us-ascii?Q?1YceuAyOd9oG9gHlLfckB6xUpjvp/pDlx48DZwXw9SdUZHgDj0iJ7Gw4E6ke?= =?us-ascii?Q?whIvLSPrwUDTsmjgxvFybT21piMAHz7V5WPvTPzyEVQdnaFLqoYqureDNZzd?= =?us-ascii?Q?mHCoPluken8wNkiYFiwowXPzbqYKU1TNMPAmEaxLV0D2nEUhfzDMIkv1ZOGZ?= =?us-ascii?Q?7h0H8HPVcKBqzYpNV/eMB3/YwAugjS7rtnTrjyU2GAg1npBU/CN7W834jBEb?= =?us-ascii?Q?637xne6K6Jw4/h4CjlDIfc8MqPJKjPLGjo8c7l8ANGvvmZ2bK6iCe9/9efNc?= =?us-ascii?Q?NliIyJXR1l6S2lCchuX8iM4p0ZPX1bypAvVA148QrNJaheEi+gEaooEIbx6A?= =?us-ascii?Q?9oSB+J+Fzuo6StEYnyPQ6jGD5jnx58+Wf1D76Zb7zBwxnNpR+Hcb3+WX+uM1?= =?us-ascii?Q?7eKatSskyU9O4YTyevt4rGJ7ayk3HXpgarRN3a5YGM/+fGQx4vquSklrZ9I3?= =?us-ascii?Q?BD4+X5lQrvxx8SNEWN4ONSTFoi+qsFkaXl34ZiiDcsrqsOITN+UCRdmKyiZh?= =?us-ascii?Q?SlsXWrsw+HOwJ43HHr6jX4y8S668MVXkIB5gN8kDVub80pWQO4fBCbYTu4Sb?= =?us-ascii?Q?fA=3D=3D?= X-OriginatorOrg: virtuozzo.com X-MS-Exchange-CrossTenant-Network-Message-Id: edd79205-a00b-4666-4ae2-08da9c9d2e72 X-MS-Exchange-CrossTenant-AuthSource: AS8PR08MB7095.eurprd08.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 22 Sep 2022 13:20:13.0637 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 0bc7f26d-0264-416e-a6fc-8352af79c58f X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: J/IRDEq/sSnhKLfCCf21BOO/thSA4lYJlwwlqs6xdexuTJz1uwyxI5TTjsxlrlgv84zPCGvDYky91P/BFVG2sjPl4kaHncJ7iV01+7eeys4= X-MS-Exchange-Transport-CrossTenantHeadersStamped: PAXPR08MB6480 Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=2a01:111:f400:fe0c::703; envelope-from=alexander.ivanov@virtuozzo.com; helo=EUR04-DB3-obe.outbound.protection.outlook.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail-DKIM: pass (identity @virtuozzo.com) X-ZM-MESSAGEID: 1663857020750100001 Content-Type: text/plain; charset="utf-8" Add commands-bsd.c file with dumb functions, fix device path and make the code buildable in FreeBSD. Signed-off-by: Alexander Ivanov --- meson.build | 2 +- qga/commands-bsd.c | 121 +++++++++++++++++++++++++++++++++++++++++++ qga/commands-posix.c | 6 ++- qga/main.c | 11 +++- qga/meson.build | 3 ++ 5 files changed, 140 insertions(+), 3 deletions(-) create mode 100644 qga/commands-bsd.c diff --git a/meson.build b/meson.build index c2adb7caf4..574cc1e91e 100644 --- a/meson.build +++ b/meson.build @@ -75,7 +75,7 @@ have_tools =3D get_option('tools') \ .allowed() have_ga =3D get_option('guest_agent') \ .disable_auto_if(not have_system and not have_tools) \ - .require(targetos in ['sunos', 'linux', 'windows'], + .require(targetos in ['sunos', 'linux', 'windows', 'freebsd'], error_message: 'unsupported OS for QEMU guest agent') \ .allowed() have_block =3D have_system or have_tools diff --git a/qga/commands-bsd.c b/qga/commands-bsd.c new file mode 100644 index 0000000000..c1e3ed13e9 --- /dev/null +++ b/qga/commands-bsd.c @@ -0,0 +1,121 @@ +/* + * QEMU Guest Agent BSD-specific command implementations + * + * Copyright (c) Virtuozzo International GmbH. + * + * Authors: + * Alexander Ivanov + * + * This work is licensed under the terms of the GNU GPL, version 2 or late= r. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qga-qapi-commands.h" +#include "qapi/qmp/qerror.h" +#include "qapi/error.h" +#include "qemu/queue.h" +#include "commands-common.h" + +#if defined(CONFIG_FSFREEZE) || defined(CONFIG_FSTRIM) +bool build_fs_mount_list(FsMountList *mounts, Error **errp) +{ + error_setg(errp, QERR_UNSUPPORTED); + return false; +} +#endif + +#if defined(CONFIG_FSFREEZE) +int64_t qmp_guest_fsfreeze_do_freeze_list(bool has_mountpoints, + strList *mountpoints, + FsMountList mounts, + Error **errp) +{ + error_setg(errp, QERR_UNSUPPORTED); + return 0; +} + +int qmp_guest_fsfreeze_do_thaw(Error **errp) +{ + error_setg(errp, QERR_UNSUPPORTED); + return 0; +} +#endif + +GuestDiskInfoList *qmp_guest_get_disks(Error **errp) +{ + error_setg(errp, QERR_UNSUPPORTED); + return NULL; +} + +GuestDiskStatsInfoList *qmp_guest_get_diskstats(Error **errp) +{ + error_setg(errp, QERR_UNSUPPORTED); + return NULL; +} + +GuestCpuStatsList *qmp_guest_get_cpustats(Error **errp) +{ + error_setg(errp, QERR_UNSUPPORTED); + return NULL; +} + +GuestFilesystemInfoList *qmp_guest_get_fsinfo(Error **errp) +{ + error_setg(errp, QERR_UNSUPPORTED); + return NULL; +} + +void qmp_guest_suspend_disk(Error **errp) +{ + error_setg(errp, QERR_UNSUPPORTED); +} + +void qmp_guest_suspend_ram(Error **errp) +{ + error_setg(errp, QERR_UNSUPPORTED); +} + +void qmp_guest_suspend_hybrid(Error **errp) +{ + error_setg(errp, QERR_UNSUPPORTED); +} + +GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp) +{ + error_setg(errp, QERR_UNSUPPORTED); + return NULL; +} + +int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp) +{ + error_setg(errp, QERR_UNSUPPORTED); + return -1; +} + +void qmp_guest_set_user_password(const char *username, + const char *password, + bool crypted, + Error **errp) +{ + error_setg(errp, QERR_UNSUPPORTED); +} + +GuestMemoryBlockList *qmp_guest_get_memory_blocks(Error **errp) +{ + error_setg(errp, QERR_UNSUPPORTED); + return NULL; +} + +GuestMemoryBlockResponseList * +qmp_guest_set_memory_blocks(GuestMemoryBlockList *mem_blks, Error **errp) +{ + error_setg(errp, QERR_UNSUPPORTED); + return NULL; +} + +GuestMemoryBlockInfo *qmp_guest_get_memory_block_info(Error **errp) +{ + error_setg(errp, QERR_UNSUPPORTED); + return NULL; +} diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 0bb8b9e2f3..3a1055d5c3 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -31,6 +31,10 @@ #include #endif =20 +#ifdef __FreeBSD__ +#undef HAVE_GETIFADDRS +#endif + #ifdef HAVE_GETIFADDRS #include #include @@ -763,7 +767,7 @@ void qmp_guest_file_flush(int64_t handle, Error **errp) } } =20 -#if !defined(__linux__) +#if !(defined(__linux__) || defined(__FreeBSD__)) =20 void qmp_guest_suspend_disk(Error **errp) { diff --git a/qga/main.c b/qga/main.c index 5f1efa2333..22b3c0df11 100644 --- a/qga/main.c +++ b/qga/main.c @@ -45,9 +45,14 @@ #endif =20 #ifndef _WIN32 +#ifdef __FreeBSD__ +#define QGA_VIRTIO_PATH_DEFAULT "/dev/vtcon/org.qemu.guest_agent.0" +#define QGA_SERIAL_PATH_DEFAULT "/dev/vtcon/org.qemu.guest_agent.0" +#else /* __FreeBSD__ */ #define QGA_VIRTIO_PATH_DEFAULT "/dev/virtio-ports/org.qemu.guest_agent.0" -#define QGA_STATE_RELATIVE_DIR "run" #define QGA_SERIAL_PATH_DEFAULT "/dev/ttyS0" +#endif /* __FreeBSD__ */ +#define QGA_STATE_RELATIVE_DIR "run" #else #define QGA_VIRTIO_PATH_DEFAULT "\\\\.\\Global\\org.qemu.guest_agent.0" #define QGA_STATE_RELATIVE_DIR "qemu-ga" @@ -1475,7 +1480,11 @@ int main(int argc, char **argv) } =20 if (config->method =3D=3D NULL) { +#ifdef CONFIG_BSD + config->method =3D g_strdup("isa-serial"); +#else config->method =3D g_strdup("virtio-serial"); +#endif } =20 socket_activation =3D check_socket_activation(); diff --git a/qga/meson.build b/qga/meson.build index 409f49a000..456ba4c29f 100644 --- a/qga/meson.build +++ b/qga/meson.build @@ -75,6 +75,9 @@ qga_ss.add(when: 'CONFIG_POSIX', if_true: files( qga_ss.add(when: 'CONFIG_LINUX', if_true: files( 'commands-linux.c', )) +qga_ss.add(when: 'CONFIG_BSD', if_true: files( + 'commands-bsd.c', +)) qga_ss.add(when: 'CONFIG_WIN32', if_true: files( 'channel-win32.c', 'commands-win32.c', --=20 2.34.1 From nobody Fri Mar 29 08:16:24 2024 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; arc=pass (i=1 dmarc=pass fromdomain=virtuozzo.com); dmarc=pass(p=quarantine dis=none) header.from=virtuozzo.com ARC-Seal: i=2; a=rsa-sha256; t=1663856379; cv=pass; d=zohomail.com; s=zohoarc; b=hY2PbSBJ1dTrRECE5bgvJMCDL+n+x0nrM/W7R23h8f5v4FXlweTEKHk/kaZNfIaLwx+aRlGAL5SH9TH3ISN/rVsvzF3kDrbFHYkMggIWPEDWHb0e3YqF4yKQ2LbM2Zwokcz5Q6+QPMwkL0ENJ+O7zVXZPFSOgPPEMRDJ8Z+o++A= ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1663856379; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=KnWNATV3S86omQsc/eiTIbd24tH6lGlT3gh9+O1nfU0=; b=ZENvVs+NNN9zAaA3m8I4mQ0LLm7f2o7zbpNUv8JAw1tH2RBgtA6wLg1VZ1GlwzVjNgoLRjbnnH+02paHpmge8utNcRaEYItIr34+hhUKZ7muhql3oGDLHMrAFecwPY3Zi9MfF8GCKm05Q+jVxHLazMJ8SJRcresaNttLHkG8Ysw= ARC-Authentication-Results: i=2; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; arc=pass (i=1 dmarc=pass fromdomain=virtuozzo.com); dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1663856379858160.85431262163866; Thu, 22 Sep 2022 07:19:39 -0700 (PDT) Received: from localhost ([::1]:57968 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1obN3B-0000yD-Ou for importer@patchew.org; Thu, 22 Sep 2022 10:19:37 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:51624) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1obM8O-0001WE-3L for qemu-devel@nongnu.org; Thu, 22 Sep 2022 09:21:06 -0400 Received: from mail-eopbgr60136.outbound.protection.outlook.com ([40.107.6.136]:7326 helo=EUR04-DB3-obe.outbound.protection.outlook.com) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1obM8K-0004pt-Fz for qemu-devel@nongnu.org; Thu, 22 Sep 2022 09:20:54 -0400 Received: from AS8PR08MB7095.eurprd08.prod.outlook.com (2603:10a6:20b:402::11) by PAXPR08MB6480.eurprd08.prod.outlook.com (2603:10a6:102:155::10) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.5654.16; Thu, 22 Sep 2022 13:20:14 +0000 Received: from AS8PR08MB7095.eurprd08.prod.outlook.com ([fe80::5174:25c7:6df8:741b]) by AS8PR08MB7095.eurprd08.prod.outlook.com ([fe80::5174:25c7:6df8:741b%8]) with mapi id 15.20.5654.016; Thu, 22 Sep 2022 13:20:14 +0000 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=dCVllDXvaxpU0HbAUSfJpJw9DV0surNFT6cREElYw3y6ri8LmjWCinzC+STdwTRRNfm5MkceC13hZbCXdHHqIVMXjRD0O77ZhAI/8/TSyEJaolncclEwIdyCXzvAJ2QRCqa58+wu5M7K1h0fHlam3BIQO/qQtc1SuN0FQkigofdcIqeE7950IGvzCFV+et2EtStxoWEmlx++B6kPxvIZvIRQvsAkWU++GAXytmInuz35OKIsUEYDErev5UU9IWcotBhegTdVDKKw71Ijm2Q6gDmiwf9EjISW1TU8ZD5ocnzBkF0Or9bv/PY15pgyQWDCHc5e9xBPzn3EsVePSsVdTg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=KnWNATV3S86omQsc/eiTIbd24tH6lGlT3gh9+O1nfU0=; b=goh+2g/FxAixKvxaStwOySFxLFZ0NSmJyV2rbs879QrP+t30IAHqsasK8L+AnSAQoFu3W3UZWzd8iPMcF01Kd6TzHD5hVlc8NtRdg880SKl55G0V/zs84JVghx8nkB/1jqf1gnzp118qLK+EZExpKVZBwpsJhRc4XqW1Z6RlmMwaOKn1Ts26zL4tLPnWEcbON2tf0qUy3QJmTj//2tCCCqtlX0lbVHrvlrWhEvyZVKYG5RZA3CeQ5MRGDO2BswBytpMZpl1I2Y/RNPF8ap/A3upYky3eZuXT/mvCwwNGcaAnw7GBLCVfq0cEgt3Z6VQHgcAhK3HK3Z1C+G9HHEXuRQ== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=virtuozzo.com; dmarc=pass action=none header.from=virtuozzo.com; dkim=pass header.d=virtuozzo.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=virtuozzo.com; s=selector2; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=KnWNATV3S86omQsc/eiTIbd24tH6lGlT3gh9+O1nfU0=; b=yZ1Xd16D/Pn2vIUNL+WwVzMgQnsKZ8YaLox5cY+RYy1PvMMq8hzv+Lmtv7DgrulSROqwrVoLJ5xnq69ePsZbePmK0Zs2QAZDaQ1QujavK8zxv84LD19g4TuEe1BxJaYs2Pjmm/SwOoKQLfxKhphCZMCXAwUkGe4PxCAav28PXcf897xqqHk3vfh6QFWJGdMyxsSiZ10drZ7SUS1J+TohfDiMn27V5I1DLqio0UttlfWgKLHjqAufDirrDH1j1ZzLDfIlKsyqi2t6Y/yYGbyyz8LNRrG54wVVQ5REz9rNyesx8AEEKEi8GchDIQ+iyzGZm47926UbT0TFhKSoj0CXVQ== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=virtuozzo.com; From: Alexander Ivanov To: qemu-devel@nongnu.org Cc: den@virtuozzo.com, michael.roth@amd.com, kkostiuk@redhat.com Subject: [PATCH 3/5] qga: Add UFS freeze/thaw support for FreeBSD Date: Thu, 22 Sep 2022 15:19:59 +0200 Message-Id: <20220922132001.940334-4-alexander.ivanov@virtuozzo.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220922132001.940334-1-alexander.ivanov@virtuozzo.com> References: <20220922132001.940334-1-alexander.ivanov@virtuozzo.com> Content-Transfer-Encoding: quoted-printable X-ClientProxiedBy: FR0P281CA0083.DEUP281.PROD.OUTLOOK.COM (2603:10a6:d10:1e::18) To AS8PR08MB7095.eurprd08.prod.outlook.com (2603:10a6:20b:402::11) MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: AS8PR08MB7095:EE_|PAXPR08MB6480:EE_ X-MS-Office365-Filtering-Correlation-Id: cdbb2e8b-0181-4085-1906-08da9c9d2ee7 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: N4BJ/Fku7bd6XLfLRA9/AEOETjN9DREI8IVPDsxBJbJo1kYDGEhjxs4OI+zryd7k+GAMYYgf2G0pLN/A1iy/DCAAAy1MQGTLoehgZxRwNcPfOtMxi/W1gsW/ZFr0P2tJzRs+bkDtGdxQtNNa0GlA2PxyHDnDiSZUjAe8WXmLil2W1B8T3MKX+cZE3r8SXmufca8sjPT8ZYVivyzgybOlCDs+YGURonkcYyatf/E1zCmAWk4p/Yb2mtANGqQ56OTwat8s+I6GhXVyDCP9fk+3Fk2D0t3eUH03y49lBpQ+ZeKZ/CVzO1NaQ9lxA+srBcnGSHFGBRdT3RsBjoMhMkVtEyBFGjOZz0ElMzsDnp2oaV26DcwsfCbdGHssWwM6uI2nFRmc78FCnsdZsQSlfZWT9yfRr7/8ro/6bLmdd6Xi2EnWllvmtwidlq2cLSit+eMxvhTljRR5N2JDz7qyKEq3TnebsclGk9wQJcNdtS6cFBq7H99Gy3eMR9pdWeDeVbrCk0+rHR2ZBKzs9lFOSml3grW5n1ecVjV4Mfi1p+gbw3c1if9hZBZRFrxJOlNU8rMtzIL53f7WOkADjCcEBwD+4rQEoGWlL8QeyV2VhETIiTNvWLr3ziviKISbA8yAOXsbX8rA6TKCzUR11ilVR7K7XebM8EaTbqFnTGo759pqUKlHtw4QWnYCaOhh+BnpYZ7p/Zj4PzPOauTjRBc6zbJ16W17ePB3qGC1EhI8UwQ7d66KjXv+p10oKA4S0HbwWWCzZ+1boC08Dzxmd58A6cEcAw== X-Forefront-Antispam-Report: CIP:255.255.255.255; CTRY:; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:AS8PR08MB7095.eurprd08.prod.outlook.com; PTR:; CAT:NONE; SFS:(13230022)(4636009)(346002)(366004)(376002)(136003)(396003)(39850400004)(451199015)(41300700001)(6486002)(478600001)(6666004)(52116002)(36756003)(66476007)(66556008)(316002)(186003)(66946007)(83380400001)(1076003)(5660300002)(2616005)(8936002)(26005)(6506007)(6916009)(86362001)(8676002)(44832011)(4326008)(2906002)(6512007)(38350700002)(38100700002); DIR:OUT; SFP:1102; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?us-ascii?Q?sYVNB6nGz3MJaJJ7rtAeQ9hNC1xelifFRaQ4ocTSaMvHGKsoCFldVw0Brfp1?= =?us-ascii?Q?F21ZTF2PMyNIfbbGBCe7gJjw0sp4XTeAP2HLxi4q9jS3g41qW5BHr7WPPL+Z?= =?us-ascii?Q?fBfE06LQeoT9F4FOrE1fzomsLUwiRB+OsCHCPYCFoq6bU2aZ8L3cmo5C2Hvb?= =?us-ascii?Q?t/JnStGNVb4GtBRxYXXjIknVkSwBMx6WI7Iw1wEdz47H59x2FxofJaUL0GXj?= =?us-ascii?Q?OnzDwik/NAPAypn1PNAjkWUUxqhzUvCtpGJhkd3wjXsD7RCZ91WnrNpEErgK?= =?us-ascii?Q?Qd8vfvARjAWWnvSbHextcuQzVTdcRCBOfN1Bo/2ukF9775YdBy+cLH3GfWaS?= =?us-ascii?Q?oQRlM11wcVxVT+2iabLe/h7v4Y2a2gg5gx9uWQSuT4eLs+z/fhVuwMibAFZ2?= =?us-ascii?Q?S+cfab7rp4EYbMP12qsm13GVrkb9BVLPy40+p1RlTJc4YJp+kmhAWZnqF9Rr?= =?us-ascii?Q?2nugtemorDkwGm+LfvB/lC3JP0P8UIJnmPK0h1Ax6wybnfhpQ7dlnutKd2yJ?= =?us-ascii?Q?WlSeJ3luKi2zo7OOW0Ve04iEQove/fNk0ZAQvuuhnWvbvn18cFKgro6nlDAR?= =?us-ascii?Q?8FqlnCs1sYpjNOiD/Mh7nQ47JZykq7sTCkMhnpCLAh//RLa/5i8vBmeGOl69?= =?us-ascii?Q?CTrX8BlWZI7LEx7eUnLw8AmrAjiekas4zBpf2Ppb9R7sJIEAX155bf/Xkwku?= =?us-ascii?Q?tJ+Lm/V8jHDKTFFHOM7pTx2c4gPgmNyycVAGvZfsFzVndxMQlNqXhStItPsN?= =?us-ascii?Q?aoYTKK7ptJaDQv97YpO1e9V6BKxzkuut6ey27cXBMpVBQF+wG2hbSlcHTOHE?= =?us-ascii?Q?vABuQiZ2C7JBRx9g5yMYJv7fD92aUfTDouHSpYEJwIqJulYSlEJ/USmfT0hU?= =?us-ascii?Q?chww0GPMv3S7FB1e13k4ai3+h5xDKV4A9qMcE7dJitC0WY61IT7bGDtG6H9P?= =?us-ascii?Q?4N2A4ArY0/bGqVaUTR0UUDjM02YSHdzvmh5MdWeQ5BUL1tQPUmUiNVPqLMWo?= =?us-ascii?Q?dOikYlKT/0V2arV68KjWNsPaHkQ9cBhjSOhH5A6CPcdiAIdeUmAduQ0NwT+T?= =?us-ascii?Q?8g4R3uIccz0UPToIaMU0UzS5njHwl17nTc0UYNN3Txwbgrbbu71XikhKJHqQ?= =?us-ascii?Q?wxZvu5SICtysMMotS79rHc1ODTR30N3Fuyh7LnrysngHBiMmThEgW24ygxqp?= =?us-ascii?Q?Qa4pITcw0s2u12fIp0CzVAurDVuACd3tBbh5rSyyWVaE6zYVQfc9/ujZr10X?= =?us-ascii?Q?l27R98pWKCNDivsYPnm5YQV5H1FW8Xh9mun8WT/yie3wYT1NClsDu4Wipf7M?= =?us-ascii?Q?GEsYEPGOMKnNMrCne3Ia6nH6ou7YP6nejYUKrH9NRXFCv0OLKvoE95jBW84W?= =?us-ascii?Q?QQSHr8XqA24gex8hs94115+RYuX/Vpa0afO1xfGiv2oc5e8zPoX/zvZYlIAx?= =?us-ascii?Q?ibu396F92Y4Hifm0GYwqAQ6V4AfEdRlpbekKJnZnFr/dUfUTYkRR76HJmdR8?= =?us-ascii?Q?Z3lF2990/m33DRMrysN5maA8q6ks2IyjbvJlD2z39dxXrtjzyvDXH+Kjd9y5?= =?us-ascii?Q?Zg5FrM6Z9Qc7Es7OTjadvLq10aAz7PIu8RiDXwTI7hLCu8QVb2Zlqb+HJUrB?= =?us-ascii?Q?mQ=3D=3D?= X-OriginatorOrg: virtuozzo.com X-MS-Exchange-CrossTenant-Network-Message-Id: cdbb2e8b-0181-4085-1906-08da9c9d2ee7 X-MS-Exchange-CrossTenant-AuthSource: AS8PR08MB7095.eurprd08.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 22 Sep 2022 13:20:14.1260 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 0bc7f26d-0264-416e-a6fc-8352af79c58f X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: GY5A6xHA2QImCatCKULwKZvv9lLqDk1gCV7HwmSDXKPZuh719c57LoBR5kjMcqSTZ9eSRidBfsJ+njodkzi4mQ/e6J1Ek/vUBnLkejOLULM= X-MS-Exchange-Transport-CrossTenantHeadersStamped: PAXPR08MB6480 Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=40.107.6.136; envelope-from=alexander.ivanov@virtuozzo.com; helo=EUR04-DB3-obe.outbound.protection.outlook.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H2=-0.001, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail-DKIM: pass (identity @virtuozzo.com) X-ZM-MESSAGEID: 1663856380975100001 Content-Type: text/plain; charset="utf-8" UFS supports FS freezing through ioctl UFSSUSPEND on /dev/ufssuspend. Freezed FS can be thawed by closing /dev/ufssuspend file descriptior. Use getmntinfo to get a list of mounted FS. Signed-off-by: Alexander Ivanov --- qga/commands-bsd.c | 109 +++++++++++++++++++++++++++++++++++++++--- qga/commands-common.h | 11 +++++ qga/main.c | 6 +++ 3 files changed, 120 insertions(+), 6 deletions(-) diff --git a/qga/commands-bsd.c b/qga/commands-bsd.c index c1e3ed13e9..5d3f46804a 100644 --- a/qga/commands-bsd.c +++ b/qga/commands-bsd.c @@ -17,28 +17,125 @@ #include "qemu/queue.h" #include "commands-common.h" =20 +#include +#include +#include +#include +#include + #if defined(CONFIG_FSFREEZE) || defined(CONFIG_FSTRIM) bool build_fs_mount_list(FsMountList *mounts, Error **errp) { - error_setg(errp, QERR_UNSUPPORTED); - return false; + FsMount *mount; + struct statfs *mntbuf, *mntp; + struct stat statbuf; + int i, count, ret; + + count =3D getmntinfo(&mntbuf, MNT_NOWAIT); + if (count =3D=3D 0) { + error_setg_errno(errp, errno, "getmntinfo failed"); + return false; + } + + for (i =3D 0; i < count; i++) { + mntp =3D &mntbuf[i]; + ret =3D stat(mntp->f_mntonname, &statbuf); + if (ret !=3D 0) { + continue; + } + + mount =3D g_new0(FsMount, 1); + + mount->dirname =3D g_strdup(mntp->f_mntonname); + mount->devtype =3D g_strdup(mntp->f_fstypename); + mount->devmajor =3D major(mount->dev); + mount->devminor =3D minor(mount->dev); + mount->fsid =3D mntp->f_fsid; + mount->dev =3D statbuf.st_dev; + + QTAILQ_INSERT_TAIL(mounts, mount, next); + } + return true; } #endif =20 #if defined(CONFIG_FSFREEZE) +static int ufssuspend_fd =3D -1; +static int ufssuspend_cnt; + int64_t qmp_guest_fsfreeze_do_freeze_list(bool has_mountpoints, strList *mountpoints, FsMountList mounts, Error **errp) { - error_setg(errp, QERR_UNSUPPORTED); - return 0; + int ret; + strList *list; + struct FsMount *mount; + + if (ufssuspend_fd !=3D -1) { + error_setg(errp, "filesystems have already frozen"); + return -1; + } + + ufssuspend_cnt =3D 0; + ufssuspend_fd =3D qemu_open(_PATH_UFSSUSPEND, O_RDWR, errp); + if (ufssuspend_fd =3D=3D -1) { + return -1; + } + + QTAILQ_FOREACH_REVERSE(mount, &mounts, next) { + /* + * To issue fsfreeze in the reverse order of mounts, check if the + * mount is listed in the list here + */ + if (has_mountpoints) { + for (list =3D mountpoints; list; list =3D list->next) { + if (strcmp(list->value, mount->dirname) =3D=3D 0) { + break; + } + } + if (!list) { + continue; + } + } + + /* Only UFS supports suspend */ + if (strcmp(mount->devtype, "ufs") !=3D 0) { + continue; + } + + ret =3D ioctl(ufssuspend_fd, UFSSUSPEND, &mount->fsid); + if (ret =3D=3D -1) { + /* + * ioctl returns EBUSY for all the FS except the first one + * that was suspended + */ + if (errno =3D=3D EBUSY) { + continue; + } + error_setg_errno(errp, errno, "failed to freeze %s", + mount->dirname); + goto error; + } + ufssuspend_cnt++; + } + return ufssuspend_cnt; +error: + close(ufssuspend_fd); + ufssuspend_fd =3D -1; + return -1; + } =20 int qmp_guest_fsfreeze_do_thaw(Error **errp) { - error_setg(errp, QERR_UNSUPPORTED); - return 0; + int ret =3D ufssuspend_cnt; + ufssuspend_cnt =3D 0; + if (ufssuspend_fd !=3D -1) { + close(ufssuspend_fd); + ufssuspend_fd =3D -1; + } + return ret; } #endif =20 diff --git a/qga/commands-common.h b/qga/commands-common.h index aa0472ea4c..c3be6db3a9 100644 --- a/qga/commands-common.h +++ b/qga/commands-common.h @@ -41,11 +41,22 @@ void ga_wait_child(pid_t pid, int *status, Error **errp= ); #endif #endif /* __linux__*/ =20 +#ifdef __FreeBSD__ +#include +#ifdef UFSSUSPEND +#define CONFIG_FSFREEZE +#endif +#endif /* __FreeBSD__ */ + #if defined(CONFIG_FSFREEZE) || defined(CONFIG_FSTRIM) typedef struct FsMount { char *dirname; char *devtype; unsigned int devmajor, devminor; +#if defined(__FreeBSD__) + dev_t dev; + fsid_t fsid; +#endif QTAILQ_ENTRY(FsMount) next; } FsMount; =20 diff --git a/qga/main.c b/qga/main.c index 22b3c0df11..ab420051fb 100644 --- a/qga/main.c +++ b/qga/main.c @@ -43,6 +43,12 @@ #define CONFIG_FSFREEZE #endif #endif +#ifdef __FreeBSD__ +#include +#ifdef UFSSUSPEND +#define CONFIG_FSFREEZE +#endif +#endif =20 #ifndef _WIN32 #ifdef __FreeBSD__ --=20 2.34.1 From nobody Fri Mar 29 08:16:24 2024 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; arc=pass (i=1 dmarc=pass fromdomain=virtuozzo.com); dmarc=pass(p=quarantine dis=none) header.from=virtuozzo.com ARC-Seal: i=2; a=rsa-sha256; t=1663854917; cv=pass; d=zohomail.com; s=zohoarc; b=gnbdw2AsnPmWSeME5WO06oZV1Mb2nE+4IPCnUXbSHuQzZnkWnw4Ignmz4uLmfcZZyPKD/ed2Ij0POaTEzh6WBMVgpRiC9TeW47J2ID5+Hwo0JgM33OAOwmNpVgmGcXC9faEzI+sjQLid75zRmmFGFT2OA+bO9l0RkMK73fDRtoE= ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1663854917; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=NEdr7wxFSVRIJNKjBaI3l1HwfauaFWtlEsxfhrZpjH0=; b=Yv51+5hHamnfL22vnLbe0isoJoNpQ4Cbo0MKU4tvBnXCcrXIZbBqP6axWJ3Hv0HfnzSKRiAgbCofXlyPaIlNyOEJlJybWhvjDhOdtKqARVUiZ4KIfl7MK4gr9fh5BZvIJMfAQeScRa0xB7xoOcljCygZFqSbUy+fzgzFe3XcjmI= ARC-Authentication-Results: i=2; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; arc=pass (i=1 dmarc=pass fromdomain=virtuozzo.com); dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1663854917451536.4538355877295; Thu, 22 Sep 2022 06:55:17 -0700 (PDT) Received: from localhost ([::1]:52326 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1obMfc-0008Db-0g for importer@patchew.org; Thu, 22 Sep 2022 09:55:16 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:51626) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1obM8g-0001Y9-6G for qemu-devel@nongnu.org; Thu, 22 Sep 2022 09:21:26 -0400 Received: from mail-eopbgr60136.outbound.protection.outlook.com ([40.107.6.136]:7326 helo=EUR04-DB3-obe.outbound.protection.outlook.com) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1obM8O-0004pt-0V for qemu-devel@nongnu.org; Thu, 22 Sep 2022 09:21:02 -0400 Received: from AS8PR08MB7095.eurprd08.prod.outlook.com (2603:10a6:20b:402::11) by PAXPR08MB6480.eurprd08.prod.outlook.com (2603:10a6:102:155::10) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.5654.16; Thu, 22 Sep 2022 13:20:15 +0000 Received: from AS8PR08MB7095.eurprd08.prod.outlook.com ([fe80::5174:25c7:6df8:741b]) by AS8PR08MB7095.eurprd08.prod.outlook.com ([fe80::5174:25c7:6df8:741b%8]) with mapi id 15.20.5654.016; Thu, 22 Sep 2022 13:20:15 +0000 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=LhFClolz/QjvEFWbPNLl+6sgV9kgu8VE2B2PonCa/kqRxlr9W9QDp41XLAYXQWifFx3+XQKkx47W0FacbfmtL4fzlUuwinud+K6UU6vnv5UnGHNlgdDlXlNCWMBvj7etAaPtPgjQ7wwZcZnhwH0x7XIcK1y/qdivJCDhFho+D8gq0WOyW4OvPWX3/o8cplkcEthYt5QCYGROKeIChbglmKVBg2FK4+g/i0O9cNSQjPeOKTOnLW9jGO2RSe02W5QgNjMcg1DWHwuSGSss/W2dMDMaLs1X5oFltLHivyOhwWy9VqAC+HrnwHbGH93q9J4sI8DvXKXr6lIrqWdZe1AUJA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=NEdr7wxFSVRIJNKjBaI3l1HwfauaFWtlEsxfhrZpjH0=; b=KGWkNFswSpqqBTtwDfYz4DxbM+tQ/wju3B3IvgxDz528szpBr+mHu4Y56oIDM2pOtMnjPgFyBDsueH+bNK7DcFTjX3VcEvaMkNW4lcQgNvS5gZo2W1OxFTqjE5ULg47G61u+AI5K/sou7QF3KWfXjJE0QyoLC6ftBXLDH0ZfoJsPfHYCdU54fVGDoLTg69sqJYqYjBwtRc+f+Lpbcsa0FYhL1mSnorwt8bYMMKy30QfJw+2P6qrvq/mCZPmekH3NosMo4Ld9YFMwqm0J5IiUITyAUVU/Dp2APIokqOP4J6n6gpYKCys5aGCOy165xvjvk74CGY/1Ddk3DB8a0g2Yeg== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=virtuozzo.com; dmarc=pass action=none header.from=virtuozzo.com; dkim=pass header.d=virtuozzo.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=virtuozzo.com; s=selector2; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=NEdr7wxFSVRIJNKjBaI3l1HwfauaFWtlEsxfhrZpjH0=; b=Wtyc61xbypG583C+g5T74KfYMHtE28R2EYO/p522WHVj77dEIyNp1tClSXxdH8WFxcZ7YEFeh1+awat7Gi7yalkufaQpg4SrGleJt817O/CXEcLoPGWyxM6r9HJGOcFnS89hXPddGRFhR5nOgclMdRmcX0LuJPJpyzVsjcWm8agEG/ffZNOuzI0+Uk7AImEj6WGlOD9LXxxMEzpcZZ3THVYiEkQHxOBgUY6NdmMwUFKhx6MYA0JPhYxiK4YkDgrfEAzaAxEN9brryBh0s7OwVqfLegfauBFkoFzwFPQuH4vw8G8MP7N7EXLbpK2o//JDIdQi7uTRAlH9z69PHGje2Q== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=virtuozzo.com; From: Alexander Ivanov To: qemu-devel@nongnu.org Cc: den@virtuozzo.com, michael.roth@amd.com, kkostiuk@redhat.com Subject: [PATCH 4/5] qga: Add shutdown/halt/reboot support for FreeBSD Date: Thu, 22 Sep 2022 15:20:00 +0200 Message-Id: <20220922132001.940334-5-alexander.ivanov@virtuozzo.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220922132001.940334-1-alexander.ivanov@virtuozzo.com> References: <20220922132001.940334-1-alexander.ivanov@virtuozzo.com> Content-Transfer-Encoding: quoted-printable X-ClientProxiedBy: FR0P281CA0083.DEUP281.PROD.OUTLOOK.COM (2603:10a6:d10:1e::18) To AS8PR08MB7095.eurprd08.prod.outlook.com (2603:10a6:20b:402::11) MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: AS8PR08MB7095:EE_|PAXPR08MB6480:EE_ X-MS-Office365-Filtering-Correlation-Id: 8c7fe1a6-8a33-4763-38e1-08da9c9d2f84 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: HuFhhZDiQjl9pvIQrTQjdHsXCq2SvCl+ZGTJyknalB/r7rNPCa/4GHJfkAorKwUOAdoCVEFUnvUK/UyBCfmuqTFd197qrRgXFbw8e+jlyrV0LcDp7e3mb7sTlLgyCevK5esOLNKj08x4wTXrE+KJZcZ9Sv7XC8ySivYVGren5Ghtnn50Lvg1QxHBIgIDVk/CpQtjiRbaQPSfaySYviwFRQBRNsPzrxwrIqiOg9E1Rtvsesmc4Er/vbzxfxRtY+VbRFRzCvV4VrgPbnTLcV2otTD08JzJIbfl+gXA+gShXbjFbxn17TUn5ttoSDDmh0xO/nWyuMay46lc/xqTFfWmggLj2RCXM28Y53IlmOMYTHFp43yudIOJczhpKTIBNbBBkEjH2d+XuazFhzvJvZr0cnLnvBZl1XQNWzLpuGODUDtaOjgLep4mTEEA9GvmF2Zxxq6uRUa7Yx2ovtCWseiee3T6uGJqMNIRyap5AmQsvkN+r5tLe7hJD4SqpdpDRU6gmEXA77lRqm/p/3yRyJPmJZjRHSMoNKABmcStWo7JlMSNorfNqFOgyu/1sVW+ML4Bel/L90BP6YVh3yTGtrI3U+MNosxgE6Z2gDoF92l50sB2pS5E3r84YoYNtQN1wDi9hQIc/sXiYXhQcZZPbjKe4e+E4C8ITc3CkFBBObp4hHqEAjRW3rSxKmeI1zF63B88uxy2xXb15zmWGvuY/ZS56RoDdbOpUuJVhycNgA6/zwv/KUDsSPp9ZEc/BhRr5StK8EBRXOwdMrNt6ZUe/2rWyQ== X-Forefront-Antispam-Report: CIP:255.255.255.255; CTRY:; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:AS8PR08MB7095.eurprd08.prod.outlook.com; PTR:; CAT:NONE; SFS:(13230022)(4636009)(346002)(366004)(376002)(136003)(396003)(39850400004)(451199015)(41300700001)(6486002)(478600001)(6666004)(52116002)(36756003)(66476007)(66556008)(316002)(186003)(66946007)(1076003)(5660300002)(2616005)(8936002)(26005)(6506007)(6916009)(86362001)(8676002)(44832011)(4326008)(2906002)(6512007)(38350700002)(38100700002); DIR:OUT; SFP:1102; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?us-ascii?Q?vU61dC9zTf3XJFMaewPnDZ6wjCS3loWhi22NpaPQxpc+mHv97Iuu78jZLCOb?= =?us-ascii?Q?4XELsJXZ81LKwHJidvagQRwXp61AkCnQnuEkmlrYDp+KdIJXkpt1nswx2cNZ?= =?us-ascii?Q?ucBNK7qQFtl/2CvI+7qhxM5dB9Sf9pGXWArkZqrG1c0CVdIIKrbY3biHSALm?= =?us-ascii?Q?CbE6R3Rcnrckeac5weduYSN7jJaUjS8dInO4hSMhCXhEFqW4NsTWMop8JB7E?= =?us-ascii?Q?U5YXqKeFMYkIMcTCuWnMjoeoSdSfXT7bIrxcHW4kQ8KG9n2QyR5YC5Q85xR3?= =?us-ascii?Q?MLp1dq9oqbu3g6bcP4M0OraNyYKa0MnbI3El+QyhDbKN0/cBSGXjBMnyxHPc?= =?us-ascii?Q?0JeT97V/s362m1dtRt50+GFmT5JC80R2/p1GPA1xYwq2/s2N8wWpPfsq0465?= =?us-ascii?Q?83qt4U24ZzKBhRP/RzkmiB3DSq8YKxRRMY4KkdWqmVtqZE5LYdC+bdtC5P0J?= =?us-ascii?Q?AmT8pCyt1bg5r/amRpvU3bZBbHEKwfyR4x6DebG5+x0PyRlmIRQTDtJwdE74?= =?us-ascii?Q?XLRayJDUeTTaxjTviG7PGLkSTjuo/ZoF4Pui+FCDSbIY0+kF+gvmZtqArEL3?= =?us-ascii?Q?X7lyaFQxxYHb/ZrUDzqAWtK6yAwnP74vVRtIfJQPd516a/01ReX4qI/rwHRE?= =?us-ascii?Q?c61lgZmo1f4tI76PAfTAkSALRpeyNyQVtwo0dRwzCyeqmGY64pYqBuqpiOzr?= =?us-ascii?Q?0pmXgGm+vQcqsHjDVhHfDtAYqoKCkHL+JTEJXbEnhGINnRu6qvfUGiqt/IQR?= =?us-ascii?Q?Q5K3TuJ0KL/ozeReHn+EZmFMyn8dJud/uiQE49rYDuiK1w/ZIHDIIOKkpC9Y?= =?us-ascii?Q?0vldjxNHbYKt9WEbewbN3d4mbXMBSIbt4w5Il3fCvwbbhqtVg5K2n+Tew7/H?= =?us-ascii?Q?o7D3+Sko3CDV56uL3LF1vYJI38reLsdlPlnttxyILRYyohbZdtHhqkcrdIa1?= =?us-ascii?Q?pNCiTUiJcyIl6eI9+X7DCZqPug4VLd7hSxFVisG8V2Jo+LW2OdawtS7e3P+U?= =?us-ascii?Q?o2hmzZtBO44SXVHG1HFDBXMKr4Gr4h2BBjx7UF40mtyhEeTvYlb83mKX8Lbj?= =?us-ascii?Q?hDTOUsg4pX6Hy5ro1Vzdrpa2m+5HtFoRLYVqyvLGZ8l9Ozqq1bhKN+7CKEgk?= =?us-ascii?Q?OMR/ffmly5nDU2XyljSA6q17METZpk7gtmuTwfeqfhGwsb0WymIoR09eK0qC?= =?us-ascii?Q?vrjODZfKiUnsRPoiaf6rj6o2+ITuJ+ZNXdDaXnrjcRe4i4iAW+eooSJTvp+9?= =?us-ascii?Q?wxsu/XwU2eafM2o4OYn41t2p432xIDlXKGojEVRN4RechOjm7gsjawk1l01h?= =?us-ascii?Q?x+3osbAfXyLTBs18DlEH+FyN/sdIcd5I9jfthm6f75h17xPRxuWQbD6VqfzS?= =?us-ascii?Q?0XjNHdVIAkZiPAat8tlJrPc90HoWnc1tA8+uullBOsBl7WiLdnlVCLH6AOzj?= =?us-ascii?Q?EVQ0eAekvjk+L3JVyhV0v/M66HDNucaCWEUoRq/kEsaJQ0HKQANQ4dRkaiF/?= =?us-ascii?Q?i5/XSy5mePDq4tzFVpZ7onQ2ZwJCUgJ/ZR8PVZLhkiom+bxawPJMH4QdRaA3?= =?us-ascii?Q?XVdFllsUZTy4sZs6VJVHX0t1bKpI+4IjXTP1MOGOP5AydljymMDHmrfkgFXX?= =?us-ascii?Q?Bg=3D=3D?= X-OriginatorOrg: virtuozzo.com X-MS-Exchange-CrossTenant-Network-Message-Id: 8c7fe1a6-8a33-4763-38e1-08da9c9d2f84 X-MS-Exchange-CrossTenant-AuthSource: AS8PR08MB7095.eurprd08.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 22 Sep 2022 13:20:15.0307 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 0bc7f26d-0264-416e-a6fc-8352af79c58f X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: gZcmoJPr8ZCZFWgaSXXS6CtsqrPLr+xCsKICvQJ/l4pj5YJx1X8TrtB2G4GvMTw/PhF/AN/vp5zuXI0itH3ACrOAlz7tL0b11mnFRk0hOF8= X-MS-Exchange-Transport-CrossTenantHeadersStamped: PAXPR08MB6480 Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=40.107.6.136; envelope-from=alexander.ivanov@virtuozzo.com; helo=EUR04-DB3-obe.outbound.protection.outlook.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H2=-0.001, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail-DKIM: pass (identity @virtuozzo.com) X-ZM-MESSAGEID: 1663854919719100001 Content-Type: text/plain; charset="utf-8" Add appropriate shutdown command arguments in qmp_guest_shutdown() for FreeBSD. Signed-off-by: Alexander Ivanov --- qga/commands-posix.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 3a1055d5c3..60cc673f25 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -242,6 +242,10 @@ void qmp_guest_shutdown(bool has_mode, const char *mod= e, Error **errp) const char *powerdown_flag =3D "-i5"; const char *halt_flag =3D "-i0"; const char *reboot_flag =3D "-i6"; +#elifdef CONFIG_BSD + const char *powerdown_flag =3D "-p"; + const char *halt_flag =3D "-h"; + const char *reboot_flag =3D "-r"; #else const char *powerdown_flag =3D "-P"; const char *halt_flag =3D "-H"; @@ -272,6 +276,9 @@ void qmp_guest_shutdown(bool has_mode, const char *mode= , Error **errp) #ifdef CONFIG_SOLARIS execl("/sbin/shutdown", "shutdown", shutdown_flag, "-g0", "-y", "hypervisor initiated shutdown", (char *)NULL); +#elifdef CONFIG_BSD + execl("/sbin/shutdown", "shutdown", shutdown_flag, "+0", + "hypervisor initiated shutdown", (char *)NULL); #else execl("/sbin/shutdown", "shutdown", "-h", shutdown_flag, "+0", "hypervisor initiated shutdown", (char *)NULL); --=20 2.34.1 From nobody Fri Mar 29 08:16:24 2024 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; arc=pass (i=1 dmarc=pass fromdomain=virtuozzo.com); dmarc=pass(p=quarantine dis=none) header.from=virtuozzo.com ARC-Seal: i=2; a=rsa-sha256; t=1663856725; cv=pass; d=zohomail.com; s=zohoarc; b=RJ/4PRHCBh7fO1Xmq+M3b6RUWPIOPAErpbYY1mTf8pZE0fvI48yezaN3oISYU0em2tgP89nvgD1/OXxFruzYLoTXE7fIwEasWG4fbOh3oXQH+vPhuiL00w0FOyIs4RVlRK6GIDCmp5/5SkOTsiYrLwiAGLwaEKtqg9477iJMfws= ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1663856725; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=qnO7doIdUC9xNB7+sP10/LMdPtsJPq+NbTdQrvdFWAA=; b=ld7TPVL0nQblgYk+0cManuILvkl0LvNzrRGsU6WtpTNLG1BGWbDhJCZ0R/Z7/Ossqb+UVSGV+Hvn+l2dyDmQrZbFmqdOK8y1h7Vw9UV56IXF7WpQC8z0DCJ+c7WAPD9tIt3j5Ajq7ZBXEFrVidJji/BKhHasY/kPQxp7FT8e8KA= ARC-Authentication-Results: i=2; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; arc=pass (i=1 dmarc=pass fromdomain=virtuozzo.com); dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1663856725115812.3819678218711; Thu, 22 Sep 2022 07:25:25 -0700 (PDT) Received: from localhost ([::1]:43130 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1obN8l-0007BI-CM for importer@patchew.org; Thu, 22 Sep 2022 10:25:23 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:46674) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1obM8h-0001YS-NB for qemu-devel@nongnu.org; Thu, 22 Sep 2022 09:21:26 -0400 Received: from mail-eopbgr60136.outbound.protection.outlook.com ([40.107.6.136]:7326 helo=EUR04-DB3-obe.outbound.protection.outlook.com) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1obM8e-0004pt-6d for qemu-devel@nongnu.org; Thu, 22 Sep 2022 09:21:15 -0400 Received: from AS8PR08MB7095.eurprd08.prod.outlook.com (2603:10a6:20b:402::11) by PAXPR08MB6480.eurprd08.prod.outlook.com (2603:10a6:102:155::10) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.5654.16; Thu, 22 Sep 2022 13:20:15 +0000 Received: from AS8PR08MB7095.eurprd08.prod.outlook.com ([fe80::5174:25c7:6df8:741b]) by AS8PR08MB7095.eurprd08.prod.outlook.com ([fe80::5174:25c7:6df8:741b%8]) with mapi id 15.20.5654.016; Thu, 22 Sep 2022 13:20:15 +0000 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=QGmWOUubNGQ/hlCB5AiDztVYD94tf2rm9oGTQfGBGYQMv4WSgH0P3JawA0L728vn0vHXzIg8dgiOHy33PgaDm2GHUeihhZSi6gjqaEJGLGhAgsrfwPwi/ALZjuIiCJHtDWcDEkQIk/iz6VR7juEPFhoii75fI0jO0frkx5aL7dcWisOjWheuDfxlOBpaZHZVio1jPiH6pHdGwlKHU41S9Mm6G4H1wKLKcKmanVXg0+IoB9qx0fPw9Odl8M3hCfHxtGKm3Ah9lgzK/TgSYeCDXXdNNJbDVrLyv4rLkMWk14w4w/cQYxFgqDylSEgH32E8MpIWmlKwGld4rDlbkhvQXw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=qnO7doIdUC9xNB7+sP10/LMdPtsJPq+NbTdQrvdFWAA=; b=QV25mLHHkJY9ADAGaCXymq0VMnNPLJ6lovR/7Q8orzb2v0j6JfQMvwnJc3YZUR9z91nA3zdc9pXEhsWHpvMej17cIF10ORgUecynM0CmEu8zSL+a+yzrPSa1drxDiC6uPqaILsZXBu7zQTermasE4oKQiMST8+3JMStVnHAp3P9xxgudpEXfv70beUNAijjzh4gnaqd+ARqPCUjvaTP53HoOlD8zsBA3O/uhCGWACivsGM09zQ+TjGsJ1yJ4i0sf7Yhp+elaQXJ8qleQ5dHvaHGkY+cbwqzecGDTerVHzrxb6J+j9TWvZLxpx3yHLoUIEtP/MA8QdvdznrVm7UI+vA== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=virtuozzo.com; dmarc=pass action=none header.from=virtuozzo.com; dkim=pass header.d=virtuozzo.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=virtuozzo.com; s=selector2; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=qnO7doIdUC9xNB7+sP10/LMdPtsJPq+NbTdQrvdFWAA=; b=ISnIRoR39DbdbJIrQI0yuZ7plQ2laS8YiV23tfanpHNJpdzyv6hGnQ6e/bSrbX28R2j9YnNB8o+Br++da7t+bCXXpss38jSqWBZliEgWQ/A2a8IOrsLT0LRLgnKuKU7T1RXgskIG0DLaOs76LYAagt2QSoA1fHXEIEkRYtk7HC46p/WxbUZjth8WmAQWNVAxqyJyYw58c2gtVFieIxVe6NOvHsbZOHZGybx65jt0nqzAlZ/tLAT2zety3eUza8ejOJlCGMhN/ofapUeXM9oZ9ok+fj1S4kgyHkTR5XIj1Lnysw+5Bm3Iwmx/D7/PAyJ7e2lDga74bygnlNbais32+Q== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=virtuozzo.com; From: Alexander Ivanov To: qemu-devel@nongnu.org Cc: den@virtuozzo.com, michael.roth@amd.com, kkostiuk@redhat.com Subject: [PATCH 5/5] qga: Add support for user password settinng in FreeeBSD Date: Thu, 22 Sep 2022 15:20:01 +0200 Message-Id: <20220922132001.940334-6-alexander.ivanov@virtuozzo.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220922132001.940334-1-alexander.ivanov@virtuozzo.com> References: <20220922132001.940334-1-alexander.ivanov@virtuozzo.com> Content-Transfer-Encoding: quoted-printable X-ClientProxiedBy: FR0P281CA0083.DEUP281.PROD.OUTLOOK.COM (2603:10a6:d10:1e::18) To AS8PR08MB7095.eurprd08.prod.outlook.com (2603:10a6:20b:402::11) MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: AS8PR08MB7095:EE_|PAXPR08MB6480:EE_ X-MS-Office365-Filtering-Correlation-Id: 6ad83f21-9034-4b18-68c0-08da9c9d2ffb X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: ZINIkIFbQIhnOgDQT7ACjtTVXKUpR7IU37sqJB2qUaEphjCb54kTkqlkjrTXZLmfgrgFI4Zd2aqHimK7u/Sp3YEWL6gZ0MTIcLPRr4sWm+gxbSPyAtX8x/NfX1/HSX/s4g6Rc53eDSxIs6c0YKWKqF5ltTz7gAxkcN0pULTcDVXlyPG56vXqfv6IIlA3tPOfNDV5SKiE5/9F6MvJlBY1LNy3F0nYFtMREQkYwTJZ4Ovt0KHsq0EXTheSyMx5nGEBwpYiUGolcpyVSKjpIZmJ4yRaDEfjK1YBUuv05LCtQ1rExJy9QF5x3z/LRNwPKOsVTAp+VpaX5XHLWxQxanIzQKZvfQwsMoPGXTMcDaPh1U6BZhRablHcZepzKW6+CvDGyFLtb3WpzUB3zkbJGRpNqjax1hAaeFpJeFQrQ70aDJ1q+kyj2KogGZ8V6cSYH+iNzrM3Q5KWYeQbsRlboJkCiz7EHdX7iqZwBeaJ9uQAIgYDQ31X677YCRKXdOtxxyR4ZMIeT/yU8CaFhXJNAF6OZpSISeZxP2Q2GdEbUWj5lLCd3ecbZy/l/rvGTUwYDV7I/IHqaMRQOauFt4lOzhE7aXphy0cHfI251NOQkotht8hZQ2y7sYBfB+eG/VFp2r2Dm+1QdtlKXCX9v3TQr2VkmDFaJHz1yykEVqc8w4AyFDlZzo2IgSa595LdwVw0+FoNCCEt+L/FLkYewV8g+9IahK4P82YQ/NAphYRV2zW34qlf0nMyxUoYw8DFvO4OlEhGAhW7LbDue/bEpCwMWmJQIw== X-Forefront-Antispam-Report: CIP:255.255.255.255; CTRY:; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:AS8PR08MB7095.eurprd08.prod.outlook.com; PTR:; CAT:NONE; SFS:(13230022)(4636009)(346002)(366004)(376002)(136003)(396003)(39850400004)(451199015)(41300700001)(6486002)(478600001)(6666004)(52116002)(36756003)(66476007)(66556008)(316002)(186003)(66946007)(83380400001)(1076003)(5660300002)(2616005)(8936002)(26005)(6506007)(6916009)(86362001)(8676002)(44832011)(4326008)(2906002)(6512007)(38350700002)(38100700002); DIR:OUT; SFP:1102; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?us-ascii?Q?hQYoJRhWRIA0xTqXZCnan0cUcwZqANkGicxN9Yp+D6BgbDM6p2tIWh4S04gM?= =?us-ascii?Q?+y6/RwaaDhZUEqp3xH7lArRG7OVgNuC52/n9LqjFagBD3NPu4iAZqgi3SYMY?= =?us-ascii?Q?grgNKms+cp3crWMAFCNbdZDOGomJwEhAmj7u8rQYDlftbZNW+K3mIsrqY+If?= =?us-ascii?Q?MB1frsTlAcgZK1U04m+/P+EVmzJ51udfiIjEFBy7ReYyALFDhD3uk851a9OW?= =?us-ascii?Q?TH1C534yZH315syi1vraQCdscrKmkl0H95euqrVvalAjU88jnc7R1qPOXADu?= =?us-ascii?Q?itht7+Xp9JXP8HaHf1BO2UyL5CAIIPQ2EQk8MG/3OzEBfDe8j+iLosRb6Nm5?= =?us-ascii?Q?5YdthT78lZSXUO1yQrby3Zqx7u6TgZL3d88n6pvtatf22hCk3zJ9lOgqQEmJ?= =?us-ascii?Q?Go2HduERVuftGxUqcW7fKd+i+H/5Sv72Ss+CDj+nt8NHpr+JbfejWvSGVY6X?= =?us-ascii?Q?6UagdfHM5EFE12DrvbX3AWdKrmcYsxUrUGjvAljCFPAw3HRemF8OH81HFVev?= =?us-ascii?Q?BDB7tJ+ypw5G1iv70kvQDkuHEaNU+waB1GL8CFpAwjU/LOtbUf/J4bfUEthd?= =?us-ascii?Q?mS8sdwzc8lNFNI4A8rZYA8kmrI4EBliQNdv5ERMWkqMenbr4lO1cES1FwA56?= =?us-ascii?Q?nSYp1epqvKvFgSXe2KE75CxLXiQkudf9Rr/tcT2mWx+gtVc7eNgs/DqVY+Y1?= =?us-ascii?Q?o3cKeQxm87Ua6usX2ZeFCvXKgSTrOiJBTUOfUcF06MGhI9HpPk1CeSMEHtEE?= =?us-ascii?Q?S8hHdUsR2YzuKbOhMsG+q4FP0gU69C8j8xk3g7pFqxY1AOKJI7vkcfOkgyI2?= =?us-ascii?Q?eBcBCD+ox05IjVVFCqog7xbluCneHHuTt2ULoj2dndcjj9FA2KuwcVfiS/kl?= =?us-ascii?Q?WaPnw//4B9He0xOpT5Kji+3PiNHR3P0Y6mBoKZ11RGmRooSgBaNq5Kp6zpPt?= =?us-ascii?Q?JmXMQvUdYcAiblE2kROX//T3l6yz4UGBUAYul0GY8BTWjgAnNb5sCpSp34op?= =?us-ascii?Q?Qpyh0MESCdAVCli+9Wsm++ynwBNkHieR0WjM7z7HG+3cIRnvPNQN9wpAKvCu?= =?us-ascii?Q?C0VRDoWCB9eQDylM8WVx/MPX9QKulO+N8Zpnqauukiu36abIrswHJZ6O31hQ?= =?us-ascii?Q?ylCccJlk5qEXpiwhPRsABeSbXMbNTzZKBFZWdmgcEJCqulq+ryN92q5Wa9jY?= =?us-ascii?Q?pxtP2WJfLDPTGY+yeGbZPS5UDOoWfnvsmWaV8Ec6ubCr7HZvEI3iY600w68f?= =?us-ascii?Q?wLmdX7fQZHBU3UkJ0ODJ/h6qdZ7nsarL1PkwZ3jUwsz4HEn8YP9CUyGd8uhn?= =?us-ascii?Q?4qFgYKAwTbcY93Ei1c1z/PFr15lcNCpLB7zTvfiExCAhranUm/1lWOT8XcmE?= =?us-ascii?Q?RTC40/W/c6EBOILKtVkRlWUteVB3e6x5MQYW7Ecmn/eSXpKOckXEnorQxStf?= =?us-ascii?Q?9qDhlclilvcDMF0Kcifw8iHlWutZbhTp+xv2QmarM4n9upX5aadmxuQBB9F4?= =?us-ascii?Q?uZ86oKJjrjg6rkLHwuRjw4DMNMzV6xo5h0are/p/4o4S4nbjwBM5K5N9NTit?= =?us-ascii?Q?xPIKtXmuzSmbvbmwVm0W//0SvJBVMeoy03oU6SWbMSSiAuBQEK5lXqLNijxN?= =?us-ascii?Q?5w=3D=3D?= X-OriginatorOrg: virtuozzo.com X-MS-Exchange-CrossTenant-Network-Message-Id: 6ad83f21-9034-4b18-68c0-08da9c9d2ffb X-MS-Exchange-CrossTenant-AuthSource: AS8PR08MB7095.eurprd08.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 22 Sep 2022 13:20:15.6868 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 0bc7f26d-0264-416e-a6fc-8352af79c58f X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: BJVNcvKeBpo+2ca/RU0jGoOhV73Gm5E7/PnqoaYgHst0qTqwfie3r3870s2lgl5Hma2/1uO+oOm+NgSAez5arGe+OOlvVF/5hI+rP/yh5E4= X-MS-Exchange-Transport-CrossTenantHeadersStamped: PAXPR08MB6480 Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=40.107.6.136; envelope-from=alexander.ivanov@virtuozzo.com; helo=EUR04-DB3-obe.outbound.protection.outlook.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H2=-0.001, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail-DKIM: pass (identity @virtuozzo.com) X-ZM-MESSAGEID: 1663856725671100001 Content-Type: text/plain; charset="utf-8" Move qmp_guest_set_user_password() to commands-posix.c under (__linux__ or __FreeBSD) condition. Add command and arguments for password setting in FreeBSD. Signed-off-by: Alexander Ivanov --- qga/commands-bsd.c | 8 --- qga/commands-linux.c | 105 -------------------------------------- qga/commands-posix.c | 117 ++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 116 insertions(+), 114 deletions(-) diff --git a/qga/commands-bsd.c b/qga/commands-bsd.c index 5d3f46804a..fa3933f2f4 100644 --- a/qga/commands-bsd.c +++ b/qga/commands-bsd.c @@ -190,14 +190,6 @@ int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList = *vcpus, Error **errp) return -1; } =20 -void qmp_guest_set_user_password(const char *username, - const char *password, - bool crypted, - Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); -} - GuestMemoryBlockList *qmp_guest_get_memory_blocks(Error **errp) { error_setg(errp, QERR_UNSUPPORTED); diff --git a/qga/commands-linux.c b/qga/commands-linux.c index 615e9a0027..1f25c80482 100644 --- a/qga/commands-linux.c +++ b/qga/commands-linux.c @@ -1629,111 +1629,6 @@ int64_t qmp_guest_set_vcpus(GuestLogicalProcessorLi= st *vcpus, Error **errp) return processed; } =20 -void qmp_guest_set_user_password(const char *username, - const char *password, - bool crypted, - Error **errp) -{ - Error *local_err =3D NULL; - char *passwd_path =3D NULL; - pid_t pid; - int status; - int datafd[2] =3D { -1, -1 }; - char *rawpasswddata =3D NULL; - size_t rawpasswdlen; - char *chpasswddata =3D NULL; - size_t chpasswdlen; - - rawpasswddata =3D (char *)qbase64_decode(password, -1, &rawpasswdlen, = errp); - if (!rawpasswddata) { - return; - } - rawpasswddata =3D g_renew(char, rawpasswddata, rawpasswdlen + 1); - rawpasswddata[rawpasswdlen] =3D '\0'; - - if (strchr(rawpasswddata, '\n')) { - error_setg(errp, "forbidden characters in raw password"); - goto out; - } - - if (strchr(username, '\n') || - strchr(username, ':')) { - error_setg(errp, "forbidden characters in username"); - goto out; - } - - chpasswddata =3D g_strdup_printf("%s:%s\n", username, rawpasswddata); - chpasswdlen =3D strlen(chpasswddata); - - passwd_path =3D g_find_program_in_path("chpasswd"); - - if (!passwd_path) { - error_setg(errp, "cannot find 'passwd' program in PATH"); - goto out; - } - - if (!g_unix_open_pipe(datafd, FD_CLOEXEC, NULL)) { - error_setg(errp, "cannot create pipe FDs"); - goto out; - } - - pid =3D fork(); - if (pid =3D=3D 0) { - close(datafd[1]); - /* child */ - setsid(); - dup2(datafd[0], 0); - reopen_fd_to_null(1); - reopen_fd_to_null(2); - - if (crypted) { - execl(passwd_path, "chpasswd", "-e", NULL); - } else { - execl(passwd_path, "chpasswd", NULL); - } - _exit(EXIT_FAILURE); - } else if (pid < 0) { - error_setg_errno(errp, errno, "failed to create child process"); - goto out; - } - close(datafd[0]); - datafd[0] =3D -1; - - if (qemu_write_full(datafd[1], chpasswddata, chpasswdlen) !=3D chpassw= dlen) { - error_setg_errno(errp, errno, "cannot write new account password"); - goto out; - } - close(datafd[1]); - datafd[1] =3D -1; - - ga_wait_child(pid, &status, &local_err); - if (local_err) { - error_propagate(errp, local_err); - goto out; - } - - if (!WIFEXITED(status)) { - error_setg(errp, "child process has terminated abnormally"); - goto out; - } - - if (WEXITSTATUS(status)) { - error_setg(errp, "child process has failed to set user password"); - goto out; - } - -out: - g_free(chpasswddata); - g_free(rawpasswddata); - g_free(passwd_path); - if (datafd[0] !=3D -1) { - close(datafd[0]); - } - if (datafd[1] !=3D -1) { - close(datafd[1]); - } -} - static void ga_read_sysfs_file(int dirfd, const char *pathname, char *buf, int size, Error **errp) { diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 60cc673f25..e8fc7bd516 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -774,8 +774,123 @@ void qmp_guest_file_flush(int64_t handle, Error **err= p) } } =20 -#if !(defined(__linux__) || defined(__FreeBSD__)) +#if defined(__linux__) || defined(__FreeBSD__) +void qmp_guest_set_user_password(const char *username, + const char *password, + bool crypted, + Error **errp) +{ + Error *local_err =3D NULL; + char *passwd_path =3D NULL; + pid_t pid; + int status; + int datafd[2] =3D { -1, -1 }; + char *rawpasswddata =3D NULL; + size_t rawpasswdlen; + char *chpasswddata =3D NULL; + size_t chpasswdlen; =20 + rawpasswddata =3D (char *)qbase64_decode(password, -1, &rawpasswdlen, = errp); + if (!rawpasswddata) { + return; + } + rawpasswddata =3D g_renew(char, rawpasswddata, rawpasswdlen + 1); + rawpasswddata[rawpasswdlen] =3D '\0'; + + if (strchr(rawpasswddata, '\n')) { + error_setg(errp, "forbidden characters in raw password"); + goto out; + } + + if (strchr(username, '\n') || + strchr(username, ':')) { + error_setg(errp, "forbidden characters in username"); + goto out; + } + +#ifdef __FreeBSD__ + chpasswddata =3D g_strdup(rawpasswddata); + passwd_path =3D g_find_program_in_path("pw"); +#else + chpasswddata =3D g_strdup_printf("%s:%s\n", username, rawpasswddata); + passwd_path =3D g_find_program_in_path("chpasswd"); +#endif + + chpasswdlen =3D strlen(chpasswddata); + + if (!passwd_path) { + error_setg(errp, "cannot find 'passwd' program in PATH"); + goto out; + } + + if (!g_unix_open_pipe(datafd, FD_CLOEXEC, NULL)) { + error_setg(errp, "cannot create pipe FDs"); + goto out; + } + + pid =3D fork(); + if (pid =3D=3D 0) { + close(datafd[1]); + /* child */ + setsid(); + dup2(datafd[0], 0); + reopen_fd_to_null(1); + reopen_fd_to_null(2); + +#ifdef __FreeBSD__ + const char *h_arg; + h_arg =3D (crypted) ? "-H" : "-h"; + execl(passwd_path, "pw", "usermod", "-n", username, h_arg, "0", NU= LL); +#else + if (crypted) { + execl(passwd_path, "chpasswd", "-e", NULL); + } else { + execl(passwd_path, "chpasswd", NULL); + } +#endif + _exit(EXIT_FAILURE); + } else if (pid < 0) { + error_setg_errno(errp, errno, "failed to create child process"); + goto out; + } + close(datafd[0]); + datafd[0] =3D -1; + + if (qemu_write_full(datafd[1], chpasswddata, chpasswdlen) !=3D chpassw= dlen) { + error_setg_errno(errp, errno, "cannot write new account password"); + goto out; + } + close(datafd[1]); + datafd[1] =3D -1; + + ga_wait_child(pid, &status, &local_err); + if (local_err) { + error_propagate(errp, local_err); + goto out; + } + + if (!WIFEXITED(status)) { + error_setg(errp, "child process has terminated abnormally"); + goto out; + } + + if (WEXITSTATUS(status)) { + error_setg(errp, "child process has failed to set user password"); + goto out; + } + +out: + g_free(chpasswddata); + g_free(rawpasswddata); + g_free(passwd_path); + if (datafd[0] !=3D -1) { + close(datafd[0]); + } + if (datafd[1] !=3D -1) { + close(datafd[1]); + } +} +#else void qmp_guest_suspend_disk(Error **errp) { error_setg(errp, QERR_UNSUPPORTED); --=20 2.34.1