2011-07-20 04:41:55 +08:00
|
|
|
/*
|
2012-01-20 12:19:27 +08:00
|
|
|
* QEMU Guest Agent POSIX-specific command implementations
|
2011-07-20 04:41:55 +08:00
|
|
|
*
|
|
|
|
* Copyright IBM Corp. 2011
|
|
|
|
*
|
|
|
|
* Authors:
|
|
|
|
* Michael Roth <mdroth@linux.vnet.ibm.com>
|
2012-03-01 00:02:23 +08:00
|
|
|
* Michal Privoznik <mprivozn@redhat.com>
|
2011-07-20 04:41:55 +08:00
|
|
|
*
|
|
|
|
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
|
|
|
* See the COPYING file in the top-level directory.
|
|
|
|
*/
|
|
|
|
|
2016-01-30 01:49:58 +08:00
|
|
|
#include "qemu/osdep.h"
|
2012-03-26 02:59:41 +08:00
|
|
|
#include <sys/ioctl.h>
|
2017-07-14 22:28:56 +08:00
|
|
|
#include <sys/utsname.h>
|
2012-05-24 02:48:05 +08:00
|
|
|
#include <sys/wait.h>
|
qga: Add guest-get-fsinfo command
Add command to get mounted filesystems information in the guest.
The returned value contains a list of mountpoint paths and
corresponding disks info such as disk bus type, drive address,
and the disk controllers' PCI addresses, so that management layer
such as libvirt can resolve the disk backends.
For example, when `lsblk' result is:
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sdb 8:16 0 1G 0 disk
`-sdb1 8:17 0 1024M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
sdc 8:32 0 1G 0 disk
`-sdc1 8:33 0 512M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
vda 252:0 0 25G 0 disk
`-vda1 252:1 0 25G 0 part /
where sdb is a SCSI disk with PCI controller 0000:00:0a.0 and ID=1,
sdc is an IDE disk with PCI controller 0000:00:01.1, and
vda is a virtio-blk disk with PCI device 0000:00:06.0,
guest-get-fsinfo command will return the following result:
{"return":
[{"name":"dm-1",
"mountpoint":"/mnt/test",
"disk":[
{"bus-type":"scsi","bus":0,"unit":1,"target":0,
"pci-controller":{"bus":0,"slot":10,"domain":0,"function":0}},
{"bus-type":"ide","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":1,"domain":0,"function":1}}],
"type":"xfs"},
{"name":"vda1", "mountpoint":"/",
"disk":[
{"bus-type":"virtio","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":6,"domain":0,"function":0}}],
"type":"ext4"}]}
In Linux guest, the disk information is resolved from sysfs. So far,
it only supports virtio-blk, virtio-scsi, IDE, SATA, SCSI disks on x86
hosts, and "disk" parameter may be empty for unsupported disk types.
Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama@hds.com>
*updated schema to report 2.2 as initial supported version
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2014-07-01 05:51:34 +08:00
|
|
|
#include <dirent.h>
|
2018-02-11 17:36:05 +08:00
|
|
|
#include "qga-qapi-commands.h"
|
2018-02-01 19:18:31 +08:00
|
|
|
#include "qapi/error.h"
|
2012-12-18 01:19:43 +08:00
|
|
|
#include "qapi/qmp/qerror.h"
|
2012-12-18 01:20:00 +08:00
|
|
|
#include "qemu/host-utils.h"
|
2015-10-28 23:13:55 +08:00
|
|
|
#include "qemu/sockets.h"
|
2015-11-23 23:37:07 +08:00
|
|
|
#include "qemu/base64.h"
|
2016-03-21 01:16:19 +08:00
|
|
|
#include "qemu/cutils.h"
|
2020-04-14 21:30:42 +08:00
|
|
|
#include "commands-common.h"
|
2022-04-20 10:26:10 +08:00
|
|
|
#include "block/nvme.h"
|
2022-05-25 22:41:31 +08:00
|
|
|
#include "cutils.h"
|
2011-07-24 05:14:37 +08:00
|
|
|
|
2017-07-17 21:58:33 +08:00
|
|
|
#ifdef HAVE_UTMPX
|
|
|
|
#include <utmpx.h>
|
|
|
|
#endif
|
|
|
|
|
2011-07-24 05:14:37 +08:00
|
|
|
#if defined(__linux__)
|
2011-07-20 04:41:55 +08:00
|
|
|
#include <mntent.h>
|
2018-06-14 16:06:06 +08:00
|
|
|
#include <sys/statvfs.h>
|
2022-04-20 10:26:10 +08:00
|
|
|
#include <linux/nvme_ioctl.h>
|
2011-07-20 04:41:55 +08:00
|
|
|
|
2018-10-23 19:23:11 +08:00
|
|
|
#ifdef CONFIG_LIBUDEV
|
|
|
|
#include <libudev.h>
|
|
|
|
#endif
|
2012-03-26 02:59:41 +08:00
|
|
|
#endif
|
|
|
|
|
2022-04-27 03:55:22 +08:00
|
|
|
#ifdef HAVE_GETIFADDRS
|
|
|
|
#include <arpa/inet.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <net/if.h>
|
2022-11-12 19:40:43 +08:00
|
|
|
#if defined(__NetBSD__) || defined(__OpenBSD__)
|
|
|
|
#include <net/if_arp.h>
|
|
|
|
#include <netinet/if_ether.h>
|
|
|
|
#else
|
2022-10-17 15:28:24 +08:00
|
|
|
#include <net/ethernet.h>
|
2022-11-12 19:40:43 +08:00
|
|
|
#endif
|
2022-04-27 03:55:22 +08:00
|
|
|
#ifdef CONFIG_SOLARIS
|
|
|
|
#include <sys/sockio.h>
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
2014-05-02 19:26:30 +08:00
|
|
|
static void ga_wait_child(pid_t pid, int *status, Error **errp)
|
2012-11-27 21:01:58 +08:00
|
|
|
{
|
|
|
|
pid_t rpid;
|
|
|
|
|
|
|
|
*status = 0;
|
|
|
|
|
2022-10-23 17:04:22 +08:00
|
|
|
rpid = RETRY_ON_EINTR(waitpid(pid, status, 0));
|
2012-11-27 21:01:58 +08:00
|
|
|
|
|
|
|
if (rpid == -1) {
|
2014-05-02 19:26:30 +08:00
|
|
|
error_setg_errno(errp, errno, "failed to wait for child (pid: %d)",
|
|
|
|
pid);
|
2012-11-27 21:01:58 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
g_assert(rpid == pid);
|
|
|
|
}
|
|
|
|
|
2022-11-05 00:07:11 +08:00
|
|
|
void qmp_guest_shutdown(const char *mode, Error **errp)
|
2011-07-20 04:41:55 +08:00
|
|
|
{
|
|
|
|
const char *shutdown_flag;
|
2012-11-27 21:01:58 +08:00
|
|
|
Error *local_err = NULL;
|
|
|
|
pid_t pid;
|
2012-05-15 02:25:20 +08:00
|
|
|
int status;
|
2011-07-20 04:41:55 +08:00
|
|
|
|
2022-04-27 03:55:26 +08:00
|
|
|
#ifdef CONFIG_SOLARIS
|
|
|
|
const char *powerdown_flag = "-i5";
|
|
|
|
const char *halt_flag = "-i0";
|
|
|
|
const char *reboot_flag = "-i6";
|
2022-10-17 15:28:22 +08:00
|
|
|
#elif defined(CONFIG_BSD)
|
|
|
|
const char *powerdown_flag = "-p";
|
|
|
|
const char *halt_flag = "-h";
|
|
|
|
const char *reboot_flag = "-r";
|
2022-04-27 03:55:26 +08:00
|
|
|
#else
|
|
|
|
const char *powerdown_flag = "-P";
|
|
|
|
const char *halt_flag = "-H";
|
|
|
|
const char *reboot_flag = "-r";
|
|
|
|
#endif
|
|
|
|
|
2011-07-20 04:41:55 +08:00
|
|
|
slog("guest-shutdown called, mode: %s", mode);
|
2022-11-05 00:07:11 +08:00
|
|
|
if (!mode || strcmp(mode, "powerdown") == 0) {
|
2022-04-27 03:55:26 +08:00
|
|
|
shutdown_flag = powerdown_flag;
|
2011-07-20 04:41:55 +08:00
|
|
|
} else if (strcmp(mode, "halt") == 0) {
|
2022-04-27 03:55:26 +08:00
|
|
|
shutdown_flag = halt_flag;
|
2011-07-20 04:41:55 +08:00
|
|
|
} else if (strcmp(mode, "reboot") == 0) {
|
2022-04-27 03:55:26 +08:00
|
|
|
shutdown_flag = reboot_flag;
|
2011-07-20 04:41:55 +08:00
|
|
|
} else {
|
2014-05-02 19:26:30 +08:00
|
|
|
error_setg(errp,
|
2012-11-27 21:01:58 +08:00
|
|
|
"mode is invalid (valid values are: halt|powerdown|reboot");
|
2011-07-20 04:41:55 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-05-12 03:19:47 +08:00
|
|
|
pid = fork();
|
|
|
|
if (pid == 0) {
|
2011-07-20 04:41:55 +08:00
|
|
|
/* child, start the shutdown */
|
|
|
|
setsid();
|
2012-05-15 02:25:20 +08:00
|
|
|
reopen_fd_to_null(0);
|
|
|
|
reopen_fd_to_null(1);
|
|
|
|
reopen_fd_to_null(2);
|
2011-07-20 04:41:55 +08:00
|
|
|
|
2022-04-27 03:55:26 +08:00
|
|
|
#ifdef CONFIG_SOLARIS
|
|
|
|
execl("/sbin/shutdown", "shutdown", shutdown_flag, "-g0", "-y",
|
|
|
|
"hypervisor initiated shutdown", (char *)NULL);
|
2022-10-17 15:28:22 +08:00
|
|
|
#elif defined(CONFIG_BSD)
|
|
|
|
execl("/sbin/shutdown", "shutdown", shutdown_flag, "+0",
|
|
|
|
"hypervisor initiated shutdown", (char *)NULL);
|
2022-04-27 03:55:26 +08:00
|
|
|
#else
|
2022-03-23 23:57:41 +08:00
|
|
|
execl("/sbin/shutdown", "shutdown", "-h", shutdown_flag, "+0",
|
|
|
|
"hypervisor initiated shutdown", (char *)NULL);
|
2022-04-27 03:55:26 +08:00
|
|
|
#endif
|
2012-05-15 02:25:20 +08:00
|
|
|
_exit(EXIT_FAILURE);
|
2012-05-12 03:19:47 +08:00
|
|
|
} else if (pid < 0) {
|
2014-05-02 19:26:30 +08:00
|
|
|
error_setg_errno(errp, errno, "failed to create child process");
|
2012-11-27 21:01:58 +08:00
|
|
|
return;
|
2011-07-20 04:41:55 +08:00
|
|
|
}
|
2012-05-12 03:19:47 +08:00
|
|
|
|
2012-11-27 21:01:58 +08:00
|
|
|
ga_wait_child(pid, &status, &local_err);
|
2014-01-30 22:07:28 +08:00
|
|
|
if (local_err) {
|
2014-05-02 19:26:30 +08:00
|
|
|
error_propagate(errp, local_err);
|
2012-11-27 21:01:58 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!WIFEXITED(status)) {
|
2014-05-02 19:26:30 +08:00
|
|
|
error_setg(errp, "child process has terminated abnormally");
|
2012-11-27 21:01:58 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (WEXITSTATUS(status)) {
|
2014-05-02 19:26:30 +08:00
|
|
|
error_setg(errp, "child process has failed to shutdown");
|
2012-05-12 03:19:47 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-03-19 01:20:07 +08:00
|
|
|
/* succeeded */
|
2011-07-20 04:41:55 +08:00
|
|
|
}
|
|
|
|
|
2014-01-31 18:29:51 +08:00
|
|
|
void qmp_guest_set_time(bool has_time, int64_t time_ns, Error **errp)
|
2013-03-05 17:39:12 +08:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
int status;
|
|
|
|
pid_t pid;
|
|
|
|
Error *local_err = NULL;
|
|
|
|
struct timeval tv;
|
2019-11-27 18:43:25 +08:00
|
|
|
static const char hwclock_path[] = "/sbin/hwclock";
|
|
|
|
static int hwclock_available = -1;
|
|
|
|
|
|
|
|
if (hwclock_available < 0) {
|
|
|
|
hwclock_available = (access(hwclock_path, X_OK) == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!hwclock_available) {
|
|
|
|
error_setg(errp, QERR_UNSUPPORTED);
|
|
|
|
return;
|
|
|
|
}
|
2013-03-05 17:39:12 +08:00
|
|
|
|
2014-01-31 18:29:51 +08:00
|
|
|
/* If user has passed a time, validate and set it. */
|
|
|
|
if (has_time) {
|
2015-07-05 22:28:58 +08:00
|
|
|
GDate date = { 0, };
|
|
|
|
|
2014-01-31 18:29:51 +08:00
|
|
|
/* year-2038 will overflow in case time_t is 32bit */
|
|
|
|
if (time_ns / 1000000000 != (time_t)(time_ns / 1000000000)) {
|
|
|
|
error_setg(errp, "Time %" PRId64 " is too large", time_ns);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
tv.tv_sec = time_ns / 1000000000;
|
|
|
|
tv.tv_usec = (time_ns % 1000000000) / 1000;
|
2015-07-05 22:28:58 +08:00
|
|
|
g_date_set_time_t(&date, tv.tv_sec);
|
|
|
|
if (date.year < 1970 || date.year >= 2070) {
|
|
|
|
error_setg_errno(errp, errno, "Invalid time");
|
|
|
|
return;
|
|
|
|
}
|
2014-01-31 18:29:51 +08:00
|
|
|
|
|
|
|
ret = settimeofday(&tv, NULL);
|
|
|
|
if (ret < 0) {
|
|
|
|
error_setg_errno(errp, errno, "Failed to set time to guest");
|
|
|
|
return;
|
|
|
|
}
|
2013-03-05 17:39:12 +08:00
|
|
|
}
|
|
|
|
|
2014-01-31 18:29:51 +08:00
|
|
|
/* Now, if user has passed a time to set and the system time is set, we
|
|
|
|
* just need to synchronize the hardware clock. However, if no time was
|
|
|
|
* passed, user is requesting the opposite: set the system time from the
|
2014-04-04 23:25:02 +08:00
|
|
|
* hardware clock (RTC). */
|
2013-03-05 17:39:12 +08:00
|
|
|
pid = fork();
|
|
|
|
if (pid == 0) {
|
|
|
|
setsid();
|
|
|
|
reopen_fd_to_null(0);
|
|
|
|
reopen_fd_to_null(1);
|
|
|
|
reopen_fd_to_null(2);
|
|
|
|
|
2014-01-31 18:29:51 +08:00
|
|
|
/* Use '/sbin/hwclock -w' to set RTC from the system time,
|
|
|
|
* or '/sbin/hwclock -s' to set the system time from RTC. */
|
2022-03-23 23:57:41 +08:00
|
|
|
execl(hwclock_path, "hwclock", has_time ? "-w" : "-s", NULL);
|
2013-03-05 17:39:12 +08:00
|
|
|
_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);
|
2014-01-30 22:07:28 +08:00
|
|
|
if (local_err) {
|
2013-03-05 17:39:12 +08:00
|
|
|
error_propagate(errp, local_err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!WIFEXITED(status)) {
|
|
|
|
error_setg(errp, "child process has terminated abnormally");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (WEXITSTATUS(status)) {
|
|
|
|
error_setg(errp, "hwclock failed to set hardware clock to system time");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-25 20:59:11 +08:00
|
|
|
typedef enum {
|
|
|
|
RW_STATE_NEW,
|
|
|
|
RW_STATE_READING,
|
|
|
|
RW_STATE_WRITING,
|
|
|
|
} RwState;
|
|
|
|
|
2020-04-14 21:30:42 +08:00
|
|
|
struct GuestFileHandle {
|
2011-07-20 04:41:55 +08:00
|
|
|
uint64_t id;
|
|
|
|
FILE *fh;
|
2015-11-25 20:59:11 +08:00
|
|
|
RwState state;
|
2011-07-20 04:41:55 +08:00
|
|
|
QTAILQ_ENTRY(GuestFileHandle) next;
|
2020-04-14 21:30:42 +08:00
|
|
|
};
|
2011-07-20 04:41:55 +08:00
|
|
|
|
|
|
|
static struct {
|
|
|
|
QTAILQ_HEAD(, GuestFileHandle) filehandles;
|
2015-10-13 23:41:19 +08:00
|
|
|
} guest_file_state = {
|
|
|
|
.filehandles = QTAILQ_HEAD_INITIALIZER(guest_file_state.filehandles),
|
|
|
|
};
|
2011-07-20 04:41:55 +08:00
|
|
|
|
2013-03-02 01:40:27 +08:00
|
|
|
static int64_t guest_file_handle_add(FILE *fh, Error **errp)
|
2011-07-20 04:41:55 +08:00
|
|
|
{
|
|
|
|
GuestFileHandle *gfh;
|
2013-03-02 01:40:27 +08:00
|
|
|
int64_t handle;
|
|
|
|
|
|
|
|
handle = ga_get_fd_handle(ga_state, errp);
|
2014-05-02 19:26:33 +08:00
|
|
|
if (handle < 0) {
|
|
|
|
return -1;
|
2013-03-02 01:40:27 +08:00
|
|
|
}
|
2011-07-20 04:41:55 +08:00
|
|
|
|
2015-09-14 19:50:44 +08:00
|
|
|
gfh = g_new0(GuestFileHandle, 1);
|
2013-03-02 01:40:27 +08:00
|
|
|
gfh->id = handle;
|
2011-07-20 04:41:55 +08:00
|
|
|
gfh->fh = fh;
|
|
|
|
QTAILQ_INSERT_TAIL(&guest_file_state.filehandles, gfh, next);
|
2013-03-02 01:40:27 +08:00
|
|
|
|
|
|
|
return handle;
|
2011-07-20 04:41:55 +08:00
|
|
|
}
|
|
|
|
|
2020-04-14 21:30:42 +08:00
|
|
|
GuestFileHandle *guest_file_handle_find(int64_t id, Error **errp)
|
2011-07-20 04:41:55 +08:00
|
|
|
{
|
|
|
|
GuestFileHandle *gfh;
|
|
|
|
|
|
|
|
QTAILQ_FOREACH(gfh, &guest_file_state.filehandles, next)
|
|
|
|
{
|
|
|
|
if (gfh->id == id) {
|
|
|
|
return gfh;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-02 19:26:30 +08:00
|
|
|
error_setg(errp, "handle '%" PRId64 "' has not been found", id);
|
2011-07-20 04:41:55 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-04-24 19:13:18 +08:00
|
|
|
typedef const char * const ccpc;
|
|
|
|
|
2013-05-08 23:31:35 +08:00
|
|
|
#ifndef O_BINARY
|
|
|
|
#define O_BINARY 0
|
|
|
|
#endif
|
|
|
|
|
2013-04-24 19:13:18 +08:00
|
|
|
/* http://pubs.opengroup.org/onlinepubs/9699919799/functions/fopen.html */
|
|
|
|
static const struct {
|
|
|
|
ccpc *forms;
|
|
|
|
int oflag_base;
|
|
|
|
} guest_file_open_modes[] = {
|
2013-05-08 23:31:35 +08:00
|
|
|
{ (ccpc[]){ "r", NULL }, O_RDONLY },
|
|
|
|
{ (ccpc[]){ "rb", NULL }, O_RDONLY | O_BINARY },
|
|
|
|
{ (ccpc[]){ "w", NULL }, O_WRONLY | O_CREAT | O_TRUNC },
|
|
|
|
{ (ccpc[]){ "wb", NULL }, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY },
|
|
|
|
{ (ccpc[]){ "a", NULL }, O_WRONLY | O_CREAT | O_APPEND },
|
|
|
|
{ (ccpc[]){ "ab", NULL }, O_WRONLY | O_CREAT | O_APPEND | O_BINARY },
|
|
|
|
{ (ccpc[]){ "r+", NULL }, O_RDWR },
|
|
|
|
{ (ccpc[]){ "rb+", "r+b", NULL }, O_RDWR | O_BINARY },
|
|
|
|
{ (ccpc[]){ "w+", NULL }, O_RDWR | O_CREAT | O_TRUNC },
|
|
|
|
{ (ccpc[]){ "wb+", "w+b", NULL }, O_RDWR | O_CREAT | O_TRUNC | O_BINARY },
|
|
|
|
{ (ccpc[]){ "a+", NULL }, O_RDWR | O_CREAT | O_APPEND },
|
|
|
|
{ (ccpc[]){ "ab+", "a+b", NULL }, O_RDWR | O_CREAT | O_APPEND | O_BINARY }
|
2013-04-24 19:13:18 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
static int
|
2014-05-02 19:26:30 +08:00
|
|
|
find_open_flag(const char *mode_str, Error **errp)
|
2013-04-24 19:13:18 +08:00
|
|
|
{
|
|
|
|
unsigned mode;
|
|
|
|
|
|
|
|
for (mode = 0; mode < ARRAY_SIZE(guest_file_open_modes); ++mode) {
|
|
|
|
ccpc *form;
|
|
|
|
|
|
|
|
form = guest_file_open_modes[mode].forms;
|
|
|
|
while (*form != NULL && strcmp(*form, mode_str) != 0) {
|
|
|
|
++form;
|
|
|
|
}
|
|
|
|
if (*form != NULL) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mode == ARRAY_SIZE(guest_file_open_modes)) {
|
2014-05-02 19:26:30 +08:00
|
|
|
error_setg(errp, "invalid file open mode '%s'", mode_str);
|
2013-04-24 19:13:18 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return guest_file_open_modes[mode].oflag_base | O_NOCTTY | O_NONBLOCK;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define DEFAULT_NEW_FILE_MODE (S_IRUSR | S_IWUSR | \
|
|
|
|
S_IRGRP | S_IWGRP | \
|
|
|
|
S_IROTH | S_IWOTH)
|
|
|
|
|
|
|
|
static FILE *
|
2014-05-02 19:26:30 +08:00
|
|
|
safe_open_or_create(const char *path, const char *mode, Error **errp)
|
2013-04-24 19:13:18 +08:00
|
|
|
{
|
|
|
|
int oflag;
|
2022-05-25 22:41:29 +08:00
|
|
|
int fd = -1;
|
|
|
|
FILE *f = NULL;
|
2013-04-24 19:13:18 +08:00
|
|
|
|
2022-05-25 22:41:29 +08:00
|
|
|
oflag = find_open_flag(mode, errp);
|
|
|
|
if (oflag < 0) {
|
|
|
|
goto end;
|
|
|
|
}
|
2013-04-24 19:13:18 +08:00
|
|
|
|
2022-05-25 22:41:29 +08:00
|
|
|
/* If the caller wants / allows creation of a new file, we implement it
|
|
|
|
* with a two step process: open() + (open() / fchmod()).
|
|
|
|
*
|
|
|
|
* First we insist on creating the file exclusively as a new file. If
|
|
|
|
* that succeeds, we're free to set any file-mode bits on it. (The
|
|
|
|
* motivation is that we want to set those file-mode bits independently
|
|
|
|
* of the current umask.)
|
|
|
|
*
|
|
|
|
* If the exclusive creation fails because the file already exists
|
|
|
|
* (EEXIST is not possible for any other reason), we just attempt to
|
|
|
|
* open the file, but in this case we won't be allowed to change the
|
|
|
|
* file-mode bits on the preexistent file.
|
|
|
|
*
|
|
|
|
* The pathname should never disappear between the two open()s in
|
|
|
|
* practice. If it happens, then someone very likely tried to race us.
|
|
|
|
* In this case just go ahead and report the ENOENT from the second
|
|
|
|
* open() to the caller.
|
|
|
|
*
|
|
|
|
* If the caller wants to open a preexistent file, then the first
|
|
|
|
* open() is decisive and its third argument is ignored, and the second
|
|
|
|
* open() and the fchmod() are never called.
|
|
|
|
*/
|
2022-05-25 22:41:31 +08:00
|
|
|
fd = qga_open_cloexec(path, oflag | ((oflag & O_CREAT) ? O_EXCL : 0), 0);
|
2022-05-25 22:41:29 +08:00
|
|
|
if (fd == -1 && errno == EEXIST) {
|
|
|
|
oflag &= ~(unsigned)O_CREAT;
|
2022-05-25 22:41:31 +08:00
|
|
|
fd = qga_open_cloexec(path, oflag, 0);
|
2022-05-25 22:41:29 +08:00
|
|
|
}
|
|
|
|
if (fd == -1) {
|
|
|
|
error_setg_errno(errp, errno,
|
|
|
|
"failed to open file '%s' (mode: '%s')",
|
|
|
|
path, mode);
|
|
|
|
goto end;
|
2013-04-24 19:13:18 +08:00
|
|
|
}
|
|
|
|
|
2022-05-25 22:41:29 +08:00
|
|
|
if ((oflag & O_CREAT) && fchmod(fd, DEFAULT_NEW_FILE_MODE) == -1) {
|
|
|
|
error_setg_errno(errp, errno, "failed to set permission "
|
|
|
|
"0%03o on new file '%s' (mode: '%s')",
|
|
|
|
(unsigned)DEFAULT_NEW_FILE_MODE, path, mode);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
f = fdopen(fd, mode);
|
|
|
|
if (f == NULL) {
|
|
|
|
error_setg_errno(errp, errno, "failed to associate stdio stream with "
|
|
|
|
"file descriptor %d, file '%s' (mode: '%s')",
|
|
|
|
fd, path, mode);
|
|
|
|
}
|
|
|
|
|
|
|
|
end:
|
|
|
|
if (f == NULL && fd != -1) {
|
|
|
|
close(fd);
|
|
|
|
if (oflag & O_CREAT) {
|
|
|
|
unlink(path);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return f;
|
2013-04-24 19:13:18 +08:00
|
|
|
}
|
|
|
|
|
2022-11-05 00:07:11 +08:00
|
|
|
int64_t qmp_guest_file_open(const char *path, const char *mode,
|
2014-05-02 19:26:30 +08:00
|
|
|
Error **errp)
|
2011-07-20 04:41:55 +08:00
|
|
|
{
|
|
|
|
FILE *fh;
|
2013-04-24 19:13:18 +08:00
|
|
|
Error *local_err = NULL;
|
2015-02-07 01:59:54 +08:00
|
|
|
int64_t handle;
|
2011-07-20 04:41:55 +08:00
|
|
|
|
2022-11-05 00:07:11 +08:00
|
|
|
if (!mode) {
|
2011-07-20 04:41:55 +08:00
|
|
|
mode = "r";
|
|
|
|
}
|
|
|
|
slog("guest-file-open called, filepath: %s, mode: %s", path, mode);
|
2013-04-24 19:13:18 +08:00
|
|
|
fh = safe_open_or_create(path, mode, &local_err);
|
|
|
|
if (local_err != NULL) {
|
2014-05-02 19:26:30 +08:00
|
|
|
error_propagate(errp, local_err);
|
2011-07-20 04:41:55 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* set fd non-blocking to avoid common use cases (like reading from a
|
|
|
|
* named pipe) from hanging the agent
|
|
|
|
*/
|
2022-04-25 21:39:06 +08:00
|
|
|
if (!g_unix_set_fd_nonblocking(fileno(fh), true, NULL)) {
|
|
|
|
fclose(fh);
|
|
|
|
error_setg_errno(errp, errno, "Failed to set FD nonblocking");
|
|
|
|
return -1;
|
|
|
|
}
|
2011-07-20 04:41:55 +08:00
|
|
|
|
2014-05-02 19:26:30 +08:00
|
|
|
handle = guest_file_handle_add(fh, errp);
|
2014-05-02 19:26:33 +08:00
|
|
|
if (handle < 0) {
|
2013-03-02 01:40:27 +08:00
|
|
|
fclose(fh);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2013-11-18 02:19:52 +08:00
|
|
|
slog("guest-file-open, handle: %" PRId64, handle);
|
2013-03-02 01:40:27 +08:00
|
|
|
return handle;
|
2011-07-20 04:41:55 +08:00
|
|
|
}
|
|
|
|
|
2014-05-02 19:26:30 +08:00
|
|
|
void qmp_guest_file_close(int64_t handle, Error **errp)
|
2011-07-20 04:41:55 +08:00
|
|
|
{
|
2014-05-02 19:26:30 +08:00
|
|
|
GuestFileHandle *gfh = guest_file_handle_find(handle, errp);
|
2011-07-20 04:41:55 +08:00
|
|
|
int ret;
|
|
|
|
|
2013-11-18 02:19:52 +08:00
|
|
|
slog("guest-file-close called, handle: %" PRId64, handle);
|
2011-07-20 04:41:55 +08:00
|
|
|
if (!gfh) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = fclose(gfh->fh);
|
2012-11-27 21:01:56 +08:00
|
|
|
if (ret == EOF) {
|
2014-05-02 19:26:30 +08:00
|
|
|
error_setg_errno(errp, errno, "failed to close handle");
|
2011-07-20 04:41:55 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
QTAILQ_REMOVE(&guest_file_state.filehandles, gfh, next);
|
2011-08-21 11:09:37 +08:00
|
|
|
g_free(gfh);
|
2011-07-20 04:41:55 +08:00
|
|
|
}
|
|
|
|
|
2020-04-14 21:30:43 +08:00
|
|
|
GuestFileRead *guest_file_read_unsafe(GuestFileHandle *gfh,
|
|
|
|
int64_t count, Error **errp)
|
2011-07-20 04:41:55 +08:00
|
|
|
{
|
|
|
|
GuestFileRead *read_data = NULL;
|
|
|
|
guchar *buf;
|
2020-04-14 21:30:43 +08:00
|
|
|
FILE *fh = gfh->fh;
|
2011-07-20 04:41:55 +08:00
|
|
|
size_t read_count;
|
|
|
|
|
2015-11-25 20:59:11 +08:00
|
|
|
/* explicitly flush when switching from writing to reading */
|
|
|
|
if (gfh->state == RW_STATE_WRITING) {
|
|
|
|
int ret = fflush(fh);
|
|
|
|
if (ret == EOF) {
|
|
|
|
error_setg_errno(errp, errno, "failed to flush file");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
gfh->state = RW_STATE_NEW;
|
|
|
|
}
|
|
|
|
|
2020-10-26 17:05:38 +08:00
|
|
|
buf = g_malloc0(count + 1);
|
2011-07-20 04:41:55 +08:00
|
|
|
read_count = fread(buf, 1, count, fh);
|
|
|
|
if (ferror(fh)) {
|
2014-05-02 19:26:30 +08:00
|
|
|
error_setg_errno(errp, errno, "failed to read file");
|
2011-07-20 04:41:55 +08:00
|
|
|
} else {
|
|
|
|
buf[read_count] = 0;
|
2015-09-14 19:50:44 +08:00
|
|
|
read_data = g_new0(GuestFileRead, 1);
|
2011-07-20 04:41:55 +08:00
|
|
|
read_data->count = read_count;
|
|
|
|
read_data->eof = feof(fh);
|
|
|
|
if (read_count) {
|
|
|
|
read_data->buf_b64 = g_base64_encode(buf, read_count);
|
|
|
|
}
|
2015-11-25 20:59:11 +08:00
|
|
|
gfh->state = RW_STATE_READING;
|
2011-07-20 04:41:55 +08:00
|
|
|
}
|
2011-08-21 11:09:37 +08:00
|
|
|
g_free(buf);
|
2011-07-20 04:41:55 +08:00
|
|
|
clearerr(fh);
|
|
|
|
|
|
|
|
return read_data;
|
|
|
|
}
|
|
|
|
|
|
|
|
GuestFileWrite *qmp_guest_file_write(int64_t handle, const char *buf_b64,
|
2014-05-02 19:26:30 +08:00
|
|
|
bool has_count, int64_t count,
|
|
|
|
Error **errp)
|
2011-07-20 04:41:55 +08:00
|
|
|
{
|
|
|
|
GuestFileWrite *write_data = NULL;
|
|
|
|
guchar *buf;
|
|
|
|
gsize buf_len;
|
|
|
|
int write_count;
|
2014-05-02 19:26:30 +08:00
|
|
|
GuestFileHandle *gfh = guest_file_handle_find(handle, errp);
|
2011-07-20 04:41:55 +08:00
|
|
|
FILE *fh;
|
|
|
|
|
|
|
|
if (!gfh) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
fh = gfh->fh;
|
2015-11-25 20:59:11 +08:00
|
|
|
|
|
|
|
if (gfh->state == RW_STATE_READING) {
|
|
|
|
int ret = fseek(fh, 0, SEEK_CUR);
|
|
|
|
if (ret == -1) {
|
|
|
|
error_setg_errno(errp, errno, "failed to seek file");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
gfh->state = RW_STATE_NEW;
|
|
|
|
}
|
|
|
|
|
2015-11-23 23:37:07 +08:00
|
|
|
buf = qbase64_decode(buf_b64, -1, &buf_len, errp);
|
|
|
|
if (!buf) {
|
|
|
|
return NULL;
|
|
|
|
}
|
2011-07-20 04:41:55 +08:00
|
|
|
|
|
|
|
if (!has_count) {
|
|
|
|
count = buf_len;
|
|
|
|
} else if (count < 0 || count > buf_len) {
|
2014-05-02 19:26:30 +08:00
|
|
|
error_setg(errp, "value '%" PRId64 "' is invalid for argument count",
|
2012-11-27 21:01:57 +08:00
|
|
|
count);
|
2011-08-21 11:09:37 +08:00
|
|
|
g_free(buf);
|
2011-07-20 04:41:55 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
write_count = fwrite(buf, 1, count, fh);
|
|
|
|
if (ferror(fh)) {
|
2014-05-02 19:26:30 +08:00
|
|
|
error_setg_errno(errp, errno, "failed to write to file");
|
2013-11-18 02:19:52 +08:00
|
|
|
slog("guest-file-write failed, handle: %" PRId64, handle);
|
2011-07-20 04:41:55 +08:00
|
|
|
} else {
|
2015-09-14 19:50:44 +08:00
|
|
|
write_data = g_new0(GuestFileWrite, 1);
|
2011-07-20 04:41:55 +08:00
|
|
|
write_data->count = write_count;
|
|
|
|
write_data->eof = feof(fh);
|
2015-11-25 20:59:11 +08:00
|
|
|
gfh->state = RW_STATE_WRITING;
|
2011-07-20 04:41:55 +08:00
|
|
|
}
|
2011-08-21 11:09:37 +08:00
|
|
|
g_free(buf);
|
2011-07-20 04:41:55 +08:00
|
|
|
clearerr(fh);
|
|
|
|
|
|
|
|
return write_data;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct GuestFileSeek *qmp_guest_file_seek(int64_t handle, int64_t offset,
|
2016-02-10 05:27:16 +08:00
|
|
|
GuestFileWhence *whence_code,
|
|
|
|
Error **errp)
|
2011-07-20 04:41:55 +08:00
|
|
|
{
|
2014-05-02 19:26:30 +08:00
|
|
|
GuestFileHandle *gfh = guest_file_handle_find(handle, errp);
|
2011-07-20 04:41:55 +08:00
|
|
|
GuestFileSeek *seek_data = NULL;
|
|
|
|
FILE *fh;
|
|
|
|
int ret;
|
2015-11-26 01:37:15 +08:00
|
|
|
int whence;
|
2016-02-10 05:27:16 +08:00
|
|
|
Error *err = NULL;
|
2011-07-20 04:41:55 +08:00
|
|
|
|
|
|
|
if (!gfh) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2015-11-26 01:37:15 +08:00
|
|
|
/* We stupidly exposed 'whence':'int' in our qapi */
|
2016-02-10 05:27:16 +08:00
|
|
|
whence = ga_parse_whence(whence_code, &err);
|
|
|
|
if (err) {
|
|
|
|
error_propagate(errp, err);
|
2015-11-26 01:37:15 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2011-07-20 04:41:55 +08:00
|
|
|
fh = gfh->fh;
|
|
|
|
ret = fseek(fh, offset, whence);
|
|
|
|
if (ret == -1) {
|
2014-05-02 19:26:30 +08:00
|
|
|
error_setg_errno(errp, errno, "failed to seek file");
|
2015-11-25 20:59:11 +08:00
|
|
|
if (errno == ESPIPE) {
|
|
|
|
/* file is non-seekable, stdio shouldn't be buffering anyways */
|
|
|
|
gfh->state = RW_STATE_NEW;
|
|
|
|
}
|
2011-07-20 04:41:55 +08:00
|
|
|
} else {
|
2014-02-21 20:36:49 +08:00
|
|
|
seek_data = g_new0(GuestFileSeek, 1);
|
2011-07-20 04:41:55 +08:00
|
|
|
seek_data->position = ftell(fh);
|
|
|
|
seek_data->eof = feof(fh);
|
2015-11-25 20:59:11 +08:00
|
|
|
gfh->state = RW_STATE_NEW;
|
2011-07-20 04:41:55 +08:00
|
|
|
}
|
|
|
|
clearerr(fh);
|
|
|
|
|
|
|
|
return seek_data;
|
|
|
|
}
|
|
|
|
|
2014-05-02 19:26:30 +08:00
|
|
|
void qmp_guest_file_flush(int64_t handle, Error **errp)
|
2011-07-20 04:41:55 +08:00
|
|
|
{
|
2014-05-02 19:26:30 +08:00
|
|
|
GuestFileHandle *gfh = guest_file_handle_find(handle, errp);
|
2011-07-20 04:41:55 +08:00
|
|
|
FILE *fh;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!gfh) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
fh = gfh->fh;
|
|
|
|
ret = fflush(fh);
|
|
|
|
if (ret == EOF) {
|
2014-05-02 19:26:30 +08:00
|
|
|
error_setg_errno(errp, errno, "failed to flush file");
|
2015-11-25 20:59:11 +08:00
|
|
|
} else {
|
|
|
|
gfh->state = RW_STATE_NEW;
|
2011-07-20 04:41:55 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-06-13 13:41:28 +08:00
|
|
|
#if defined(CONFIG_FSFREEZE) || defined(CONFIG_FSTRIM)
|
2022-10-17 15:28:20 +08:00
|
|
|
void free_fs_mount_list(FsMountList *mounts)
|
2012-04-17 08:52:17 +08:00
|
|
|
{
|
2012-06-13 13:41:27 +08:00
|
|
|
FsMount *mount, *temp;
|
2012-04-17 08:52:17 +08:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
2012-06-13 13:41:28 +08:00
|
|
|
#endif
|
|
|
|
|
2022-10-17 15:28:21 +08:00
|
|
|
#if defined(CONFIG_FSFREEZE)
|
|
|
|
typedef enum {
|
|
|
|
FSFREEZE_HOOK_THAW = 0,
|
|
|
|
FSFREEZE_HOOK_FREEZE,
|
|
|
|
} FsfreezeHookArg;
|
|
|
|
|
|
|
|
static const char *fsfreeze_hook_arg_string[] = {
|
|
|
|
"thaw",
|
|
|
|
"freeze",
|
|
|
|
};
|
|
|
|
|
|
|
|
static void execute_fsfreeze_hook(FsfreezeHookArg arg, Error **errp)
|
|
|
|
{
|
|
|
|
int status;
|
|
|
|
pid_t pid;
|
|
|
|
const char *hook;
|
|
|
|
const char *arg_str = fsfreeze_hook_arg_string[arg];
|
|
|
|
Error *local_err = NULL;
|
|
|
|
|
|
|
|
hook = ga_fsfreeze_hook(ga_state);
|
|
|
|
if (!hook) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (access(hook, X_OK) != 0) {
|
|
|
|
error_setg_errno(errp, errno, "can't access fsfreeze hook '%s'", hook);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
slog("executing fsfreeze hook with arg '%s'", arg_str);
|
|
|
|
pid = fork();
|
|
|
|
if (pid == 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 = 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);
|
|
|
|
}
|
|
|
|
|
|
|
|
int64_t qmp_guest_fsfreeze_freeze_list(bool has_mountpoints,
|
|
|
|
strList *mountpoints,
|
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
FsMountList mounts;
|
|
|
|
Error *local_err = 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 = 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 == 0) {
|
|
|
|
ga_unset_frozen(ga_state);
|
|
|
|
} else if (ret < 0) {
|
|
|
|
qmp_guest_fsfreeze_thaw(NULL);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int64_t qmp_guest_fsfreeze_thaw(Error **errp)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = qmp_guest_fsfreeze_do_thaw(errp);
|
|
|
|
if (ret >= 0) {
|
|
|
|
ga_unset_frozen(ga_state);
|
|
|
|
execute_fsfreeze_hook(FSFREEZE_HOOK_THAW, errp);
|
|
|
|
} else {
|
|
|
|
ret = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void guest_fsfreeze_cleanup(void)
|
|
|
|
{
|
|
|
|
Error *err = NULL;
|
|
|
|
|
|
|
|
if (ga_is_frozen(ga_state) == 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
|
|
|
|
|
|
|
|
/* linux-specific implementations. avoid this if at all possible. */
|
|
|
|
#if defined(__linux__)
|
2012-06-13 13:41:28 +08:00
|
|
|
#if defined(CONFIG_FSFREEZE)
|
2011-07-20 04:41:55 +08:00
|
|
|
|
qga: Add guest-get-fsinfo command
Add command to get mounted filesystems information in the guest.
The returned value contains a list of mountpoint paths and
corresponding disks info such as disk bus type, drive address,
and the disk controllers' PCI addresses, so that management layer
such as libvirt can resolve the disk backends.
For example, when `lsblk' result is:
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sdb 8:16 0 1G 0 disk
`-sdb1 8:17 0 1024M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
sdc 8:32 0 1G 0 disk
`-sdc1 8:33 0 512M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
vda 252:0 0 25G 0 disk
`-vda1 252:1 0 25G 0 part /
where sdb is a SCSI disk with PCI controller 0000:00:0a.0 and ID=1,
sdc is an IDE disk with PCI controller 0000:00:01.1, and
vda is a virtio-blk disk with PCI device 0000:00:06.0,
guest-get-fsinfo command will return the following result:
{"return":
[{"name":"dm-1",
"mountpoint":"/mnt/test",
"disk":[
{"bus-type":"scsi","bus":0,"unit":1,"target":0,
"pci-controller":{"bus":0,"slot":10,"domain":0,"function":0}},
{"bus-type":"ide","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":1,"domain":0,"function":1}}],
"type":"xfs"},
{"name":"vda1", "mountpoint":"/",
"disk":[
{"bus-type":"virtio","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":6,"domain":0,"function":0}}],
"type":"ext4"}]}
In Linux guest, the disk information is resolved from sysfs. So far,
it only supports virtio-blk, virtio-scsi, IDE, SATA, SCSI disks on x86
hosts, and "disk" parameter may be empty for unsupported disk types.
Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama@hds.com>
*updated schema to report 2.2 as initial supported version
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2014-07-01 05:51:34 +08:00
|
|
|
static char *get_pci_driver(char const *syspath, int pathlen, Error **errp)
|
|
|
|
{
|
|
|
|
char *path;
|
|
|
|
char *dpath;
|
|
|
|
char *driver = NULL;
|
|
|
|
char buf[PATH_MAX];
|
|
|
|
ssize_t len;
|
|
|
|
|
|
|
|
path = g_strndup(syspath, pathlen);
|
|
|
|
dpath = g_strdup_printf("%s/driver", path);
|
|
|
|
len = readlink(dpath, buf, sizeof(buf) - 1);
|
|
|
|
if (len != -1) {
|
|
|
|
buf[len] = 0;
|
2018-03-01 15:08:06 +08:00
|
|
|
driver = g_path_get_basename(buf);
|
qga: Add guest-get-fsinfo command
Add command to get mounted filesystems information in the guest.
The returned value contains a list of mountpoint paths and
corresponding disks info such as disk bus type, drive address,
and the disk controllers' PCI addresses, so that management layer
such as libvirt can resolve the disk backends.
For example, when `lsblk' result is:
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sdb 8:16 0 1G 0 disk
`-sdb1 8:17 0 1024M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
sdc 8:32 0 1G 0 disk
`-sdc1 8:33 0 512M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
vda 252:0 0 25G 0 disk
`-vda1 252:1 0 25G 0 part /
where sdb is a SCSI disk with PCI controller 0000:00:0a.0 and ID=1,
sdc is an IDE disk with PCI controller 0000:00:01.1, and
vda is a virtio-blk disk with PCI device 0000:00:06.0,
guest-get-fsinfo command will return the following result:
{"return":
[{"name":"dm-1",
"mountpoint":"/mnt/test",
"disk":[
{"bus-type":"scsi","bus":0,"unit":1,"target":0,
"pci-controller":{"bus":0,"slot":10,"domain":0,"function":0}},
{"bus-type":"ide","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":1,"domain":0,"function":1}}],
"type":"xfs"},
{"name":"vda1", "mountpoint":"/",
"disk":[
{"bus-type":"virtio","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":6,"domain":0,"function":0}}],
"type":"ext4"}]}
In Linux guest, the disk information is resolved from sysfs. So far,
it only supports virtio-blk, virtio-scsi, IDE, SATA, SCSI disks on x86
hosts, and "disk" parameter may be empty for unsupported disk types.
Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama@hds.com>
*updated schema to report 2.2 as initial supported version
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2014-07-01 05:51:34 +08:00
|
|
|
}
|
|
|
|
g_free(dpath);
|
|
|
|
g_free(path);
|
|
|
|
return driver;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int compare_uint(const void *_a, const void *_b)
|
|
|
|
{
|
|
|
|
unsigned int a = *(unsigned int *)_a;
|
|
|
|
unsigned int b = *(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 = 0;
|
|
|
|
|
|
|
|
path = g_strndup(syspath, host - syspath);
|
|
|
|
dir = opendir(path);
|
|
|
|
if (!dir) {
|
|
|
|
error_setg_errno(errp, errno, "opendir(\"%s\")", path);
|
|
|
|
g_free(path);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (i < hosts_max) {
|
|
|
|
entry = readdir(dir);
|
|
|
|
if (!entry) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (ata && sscanf(entry->d_name, "ata%d", hosts + i) == 1) {
|
|
|
|
++i;
|
|
|
|
} else if (!ata && sscanf(entry->d_name, "host%d", hosts + i) == 1) {
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
qsort(hosts, i, sizeof(hosts[0]), compare_uint);
|
|
|
|
|
|
|
|
g_free(path);
|
|
|
|
closedir(dir);
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
2020-07-22 12:40:26 +08:00
|
|
|
/*
|
|
|
|
* 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)
|
qga: Add guest-get-fsinfo command
Add command to get mounted filesystems information in the guest.
The returned value contains a list of mountpoint paths and
corresponding disks info such as disk bus type, drive address,
and the disk controllers' PCI addresses, so that management layer
such as libvirt can resolve the disk backends.
For example, when `lsblk' result is:
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sdb 8:16 0 1G 0 disk
`-sdb1 8:17 0 1024M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
sdc 8:32 0 1G 0 disk
`-sdc1 8:33 0 512M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
vda 252:0 0 25G 0 disk
`-vda1 252:1 0 25G 0 part /
where sdb is a SCSI disk with PCI controller 0000:00:0a.0 and ID=1,
sdc is an IDE disk with PCI controller 0000:00:01.1, and
vda is a virtio-blk disk with PCI device 0000:00:06.0,
guest-get-fsinfo command will return the following result:
{"return":
[{"name":"dm-1",
"mountpoint":"/mnt/test",
"disk":[
{"bus-type":"scsi","bus":0,"unit":1,"target":0,
"pci-controller":{"bus":0,"slot":10,"domain":0,"function":0}},
{"bus-type":"ide","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":1,"domain":0,"function":1}}],
"type":"xfs"},
{"name":"vda1", "mountpoint":"/",
"disk":[
{"bus-type":"virtio","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":6,"domain":0,"function":0}}],
"type":"ext4"}]}
In Linux guest, the disk information is resolved from sysfs. So far,
it only supports virtio-blk, virtio-scsi, IDE, SATA, SCSI disks on x86
hosts, and "disk" parameter may be empty for unsupported disk types.
Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama@hds.com>
*updated schema to report 2.2 as initial supported version
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2014-07-01 05:51:34 +08:00
|
|
|
{
|
|
|
|
unsigned int pci[4], host, hosts[8], tgt[3];
|
|
|
|
int i, nhosts = 0, pcilen;
|
2020-07-22 12:40:26 +08:00
|
|
|
GuestPCIAddress *pciaddr = disk->pci_controller;
|
qga: Add guest-get-fsinfo command
Add command to get mounted filesystems information in the guest.
The returned value contains a list of mountpoint paths and
corresponding disks info such as disk bus type, drive address,
and the disk controllers' PCI addresses, so that management layer
such as libvirt can resolve the disk backends.
For example, when `lsblk' result is:
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sdb 8:16 0 1G 0 disk
`-sdb1 8:17 0 1024M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
sdc 8:32 0 1G 0 disk
`-sdc1 8:33 0 512M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
vda 252:0 0 25G 0 disk
`-vda1 252:1 0 25G 0 part /
where sdb is a SCSI disk with PCI controller 0000:00:0a.0 and ID=1,
sdc is an IDE disk with PCI controller 0000:00:01.1, and
vda is a virtio-blk disk with PCI device 0000:00:06.0,
guest-get-fsinfo command will return the following result:
{"return":
[{"name":"dm-1",
"mountpoint":"/mnt/test",
"disk":[
{"bus-type":"scsi","bus":0,"unit":1,"target":0,
"pci-controller":{"bus":0,"slot":10,"domain":0,"function":0}},
{"bus-type":"ide","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":1,"domain":0,"function":1}}],
"type":"xfs"},
{"name":"vda1", "mountpoint":"/",
"disk":[
{"bus-type":"virtio","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":6,"domain":0,"function":0}}],
"type":"ext4"}]}
In Linux guest, the disk information is resolved from sysfs. So far,
it only supports virtio-blk, virtio-scsi, IDE, SATA, SCSI disks on x86
hosts, and "disk" parameter may be empty for unsupported disk types.
Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama@hds.com>
*updated schema to report 2.2 as initial supported version
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2014-07-01 05:51:34 +08:00
|
|
|
bool has_ata = false, has_host = false, has_tgt = false;
|
|
|
|
char *p, *q, *driver = NULL;
|
2020-07-22 12:40:26 +08:00
|
|
|
bool ret = false;
|
qga: Add guest-get-fsinfo command
Add command to get mounted filesystems information in the guest.
The returned value contains a list of mountpoint paths and
corresponding disks info such as disk bus type, drive address,
and the disk controllers' PCI addresses, so that management layer
such as libvirt can resolve the disk backends.
For example, when `lsblk' result is:
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sdb 8:16 0 1G 0 disk
`-sdb1 8:17 0 1024M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
sdc 8:32 0 1G 0 disk
`-sdc1 8:33 0 512M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
vda 252:0 0 25G 0 disk
`-vda1 252:1 0 25G 0 part /
where sdb is a SCSI disk with PCI controller 0000:00:0a.0 and ID=1,
sdc is an IDE disk with PCI controller 0000:00:01.1, and
vda is a virtio-blk disk with PCI device 0000:00:06.0,
guest-get-fsinfo command will return the following result:
{"return":
[{"name":"dm-1",
"mountpoint":"/mnt/test",
"disk":[
{"bus-type":"scsi","bus":0,"unit":1,"target":0,
"pci-controller":{"bus":0,"slot":10,"domain":0,"function":0}},
{"bus-type":"ide","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":1,"domain":0,"function":1}}],
"type":"xfs"},
{"name":"vda1", "mountpoint":"/",
"disk":[
{"bus-type":"virtio","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":6,"domain":0,"function":0}}],
"type":"ext4"}]}
In Linux guest, the disk information is resolved from sysfs. So far,
it only supports virtio-blk, virtio-scsi, IDE, SATA, SCSI disks on x86
hosts, and "disk" parameter may be empty for unsupported disk types.
Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama@hds.com>
*updated schema to report 2.2 as initial supported version
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2014-07-01 05:51:34 +08:00
|
|
|
|
|
|
|
p = 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) {
|
qemu-ga: make get-fsinfo work over pci bridges
Iterate over the PCI bridges to lookup the PCI device associated with
the block device.
This allows to lookup the driver under the following syspath:
/sys/devices/pci0000:00/0000:00:02.2/0000:03:00.0/virtio2/block/vda/vda3
It also works with an "old-style" Q35 libvirt hierarchy: root complex
-> DMI-PCI bridge -> PCI-PCI bridge -> virtio controller, ex:
/sys/devices/pci0000:00/0000:00:03.0/0000:01:01.0/0000:02:01.0/virtio1/block/vda/vda3
The setup can be reproduced with the following qemu command line
(Thanks Marcel for help):
qemu-system-x86_64 -M q35 \
-device i82801b11-bridge,id=dmi2pci_bridge,bus=pcie.0
-device pci-bridge,id=pci_bridge,bus=dmi2pci_bridge,addr=0x1,chassis_nr=1
-device virtio-blk-pci,scsi=off,drive=drive-virtio-disk0,id=virtio-disk0,bootindex=1,bus=pci_bridge,addr=0x1
For consistency with other syspath-related debug messages, replace a
\"%s\" in the message with '%s'.
Fixes:
https://bugzilla.redhat.com/show_bug.cgi?id=1567041
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2018-05-14 20:38:55 +08:00
|
|
|
g_debug("only pci device is supported: sysfs path '%s'", syspath);
|
2020-07-22 12:40:26 +08:00
|
|
|
return false;
|
qga: Add guest-get-fsinfo command
Add command to get mounted filesystems information in the guest.
The returned value contains a list of mountpoint paths and
corresponding disks info such as disk bus type, drive address,
and the disk controllers' PCI addresses, so that management layer
such as libvirt can resolve the disk backends.
For example, when `lsblk' result is:
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sdb 8:16 0 1G 0 disk
`-sdb1 8:17 0 1024M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
sdc 8:32 0 1G 0 disk
`-sdc1 8:33 0 512M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
vda 252:0 0 25G 0 disk
`-vda1 252:1 0 25G 0 part /
where sdb is a SCSI disk with PCI controller 0000:00:0a.0 and ID=1,
sdc is an IDE disk with PCI controller 0000:00:01.1, and
vda is a virtio-blk disk with PCI device 0000:00:06.0,
guest-get-fsinfo command will return the following result:
{"return":
[{"name":"dm-1",
"mountpoint":"/mnt/test",
"disk":[
{"bus-type":"scsi","bus":0,"unit":1,"target":0,
"pci-controller":{"bus":0,"slot":10,"domain":0,"function":0}},
{"bus-type":"ide","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":1,"domain":0,"function":1}}],
"type":"xfs"},
{"name":"vda1", "mountpoint":"/",
"disk":[
{"bus-type":"virtio","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":6,"domain":0,"function":0}}],
"type":"ext4"}]}
In Linux guest, the disk information is resolved from sysfs. So far,
it only supports virtio-blk, virtio-scsi, IDE, SATA, SCSI disks on x86
hosts, and "disk" parameter may be empty for unsupported disk types.
Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama@hds.com>
*updated schema to report 2.2 as initial supported version
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2014-07-01 05:51:34 +08:00
|
|
|
}
|
|
|
|
|
qemu-ga: make get-fsinfo work over pci bridges
Iterate over the PCI bridges to lookup the PCI device associated with
the block device.
This allows to lookup the driver under the following syspath:
/sys/devices/pci0000:00/0000:00:02.2/0000:03:00.0/virtio2/block/vda/vda3
It also works with an "old-style" Q35 libvirt hierarchy: root complex
-> DMI-PCI bridge -> PCI-PCI bridge -> virtio controller, ex:
/sys/devices/pci0000:00/0000:00:03.0/0000:01:01.0/0000:02:01.0/virtio1/block/vda/vda3
The setup can be reproduced with the following qemu command line
(Thanks Marcel for help):
qemu-system-x86_64 -M q35 \
-device i82801b11-bridge,id=dmi2pci_bridge,bus=pcie.0
-device pci-bridge,id=pci_bridge,bus=dmi2pci_bridge,addr=0x1,chassis_nr=1
-device virtio-blk-pci,scsi=off,drive=drive-virtio-disk0,id=virtio-disk0,bootindex=1,bus=pci_bridge,addr=0x1
For consistency with other syspath-related debug messages, replace a
\"%s\" in the message with '%s'.
Fixes:
https://bugzilla.redhat.com/show_bug.cgi?id=1567041
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2018-05-14 20:38:55 +08:00
|
|
|
p += 12 + pcilen;
|
|
|
|
while (true) {
|
|
|
|
driver = 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") ||
|
qga: Introduce NVMe disk bus type
Assigning a NVMe disk by VFIO or emulating a NVMe controller by QEMU,
a NVMe disk get exposed in guest side. Support NVMe disk bus type and
implement posix version.
Test PCI passthrough case:
~#virsh qemu-agent-command buster '{"execute":"guest-get-disks"}' | jq
...
{
"name": "/dev/nvme0n1",
"dependencies": [],
"partition": false,
"address": {
"serial": "SAMSUNG MZQL23T8HCLS-00A07_S64HNE0N500076",
"bus-type": "nvme",
"bus": 0,
"unit": 0,
"pci-controller": {
"bus": 0,
"slot": 22,
"domain": 0,
"function": 0
},
"dev": "/dev/nvme0n1",
"target": 0
}
...
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
Signed-off-by: zhenwei pi <pizhenwei@bytedance.com>
Message-Id: <20220420022610.418052-2-pizhenwei@bytedance.com>
2022-04-20 10:26:09 +08:00
|
|
|
g_str_equal(driver, "ahci") ||
|
2023-01-23 21:30:49 +08:00
|
|
|
g_str_equal(driver, "nvme") ||
|
|
|
|
g_str_equal(driver, "xhci_hcd") ||
|
|
|
|
g_str_equal(driver, "ehci-pci"))) {
|
qemu-ga: make get-fsinfo work over pci bridges
Iterate over the PCI bridges to lookup the PCI device associated with
the block device.
This allows to lookup the driver under the following syspath:
/sys/devices/pci0000:00/0000:00:02.2/0000:03:00.0/virtio2/block/vda/vda3
It also works with an "old-style" Q35 libvirt hierarchy: root complex
-> DMI-PCI bridge -> PCI-PCI bridge -> virtio controller, ex:
/sys/devices/pci0000:00/0000:00:03.0/0000:01:01.0/0000:02:01.0/virtio1/block/vda/vda3
The setup can be reproduced with the following qemu command line
(Thanks Marcel for help):
qemu-system-x86_64 -M q35 \
-device i82801b11-bridge,id=dmi2pci_bridge,bus=pcie.0
-device pci-bridge,id=pci_bridge,bus=dmi2pci_bridge,addr=0x1,chassis_nr=1
-device virtio-blk-pci,scsi=off,drive=drive-virtio-disk0,id=virtio-disk0,bootindex=1,bus=pci_bridge,addr=0x1
For consistency with other syspath-related debug messages, replace a
\"%s\" in the message with '%s'.
Fixes:
https://bugzilla.redhat.com/show_bug.cgi?id=1567041
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2018-05-14 20:38:55 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2018-07-06 00:16:29 +08:00
|
|
|
g_free(driver);
|
qemu-ga: make get-fsinfo work over pci bridges
Iterate over the PCI bridges to lookup the PCI device associated with
the block device.
This allows to lookup the driver under the following syspath:
/sys/devices/pci0000:00/0000:00:02.2/0000:03:00.0/virtio2/block/vda/vda3
It also works with an "old-style" Q35 libvirt hierarchy: root complex
-> DMI-PCI bridge -> PCI-PCI bridge -> virtio controller, ex:
/sys/devices/pci0000:00/0000:00:03.0/0000:01:01.0/0000:02:01.0/virtio1/block/vda/vda3
The setup can be reproduced with the following qemu command line
(Thanks Marcel for help):
qemu-system-x86_64 -M q35 \
-device i82801b11-bridge,id=dmi2pci_bridge,bus=pcie.0
-device pci-bridge,id=pci_bridge,bus=dmi2pci_bridge,addr=0x1,chassis_nr=1
-device virtio-blk-pci,scsi=off,drive=drive-virtio-disk0,id=virtio-disk0,bootindex=1,bus=pci_bridge,addr=0x1
For consistency with other syspath-related debug messages, replace a
\"%s\" in the message with '%s'.
Fixes:
https://bugzilla.redhat.com/show_bug.cgi?id=1567041
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2018-05-14 20:38:55 +08:00
|
|
|
if (sscanf(p, "/%x:%x:%x.%x%n",
|
|
|
|
pci, pci + 1, pci + 2, pci + 3, &pcilen) == 4) {
|
|
|
|
p += pcilen;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
g_debug("unsupported driver or sysfs path '%s'", syspath);
|
2020-07-22 12:40:26 +08:00
|
|
|
return false;
|
qga: Add guest-get-fsinfo command
Add command to get mounted filesystems information in the guest.
The returned value contains a list of mountpoint paths and
corresponding disks info such as disk bus type, drive address,
and the disk controllers' PCI addresses, so that management layer
such as libvirt can resolve the disk backends.
For example, when `lsblk' result is:
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sdb 8:16 0 1G 0 disk
`-sdb1 8:17 0 1024M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
sdc 8:32 0 1G 0 disk
`-sdc1 8:33 0 512M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
vda 252:0 0 25G 0 disk
`-vda1 252:1 0 25G 0 part /
where sdb is a SCSI disk with PCI controller 0000:00:0a.0 and ID=1,
sdc is an IDE disk with PCI controller 0000:00:01.1, and
vda is a virtio-blk disk with PCI device 0000:00:06.0,
guest-get-fsinfo command will return the following result:
{"return":
[{"name":"dm-1",
"mountpoint":"/mnt/test",
"disk":[
{"bus-type":"scsi","bus":0,"unit":1,"target":0,
"pci-controller":{"bus":0,"slot":10,"domain":0,"function":0}},
{"bus-type":"ide","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":1,"domain":0,"function":1}}],
"type":"xfs"},
{"name":"vda1", "mountpoint":"/",
"disk":[
{"bus-type":"virtio","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":6,"domain":0,"function":0}}],
"type":"ext4"}]}
In Linux guest, the disk information is resolved from sysfs. So far,
it only supports virtio-blk, virtio-scsi, IDE, SATA, SCSI disks on x86
hosts, and "disk" parameter may be empty for unsupported disk types.
Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama@hds.com>
*updated schema to report 2.2 as initial supported version
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2014-07-01 05:51:34 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
p = strstr(syspath, "/target");
|
|
|
|
if (p && sscanf(p + 7, "%*u:%*u:%*u/%*u:%u:%u:%u",
|
|
|
|
tgt, tgt + 1, tgt + 2) == 3) {
|
|
|
|
has_tgt = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
p = strstr(syspath, "/ata");
|
|
|
|
if (p) {
|
|
|
|
q = p + 4;
|
|
|
|
has_ata = true;
|
|
|
|
} else {
|
|
|
|
p = strstr(syspath, "/host");
|
|
|
|
q = p + 5;
|
|
|
|
}
|
|
|
|
if (p && sscanf(q, "%u", &host) == 1) {
|
|
|
|
has_host = true;
|
|
|
|
nhosts = build_hosts(syspath, p, has_ata, hosts,
|
2017-07-18 14:09:57 +08:00
|
|
|
ARRAY_SIZE(hosts), errp);
|
qga: Add guest-get-fsinfo command
Add command to get mounted filesystems information in the guest.
The returned value contains a list of mountpoint paths and
corresponding disks info such as disk bus type, drive address,
and the disk controllers' PCI addresses, so that management layer
such as libvirt can resolve the disk backends.
For example, when `lsblk' result is:
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sdb 8:16 0 1G 0 disk
`-sdb1 8:17 0 1024M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
sdc 8:32 0 1G 0 disk
`-sdc1 8:33 0 512M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
vda 252:0 0 25G 0 disk
`-vda1 252:1 0 25G 0 part /
where sdb is a SCSI disk with PCI controller 0000:00:0a.0 and ID=1,
sdc is an IDE disk with PCI controller 0000:00:01.1, and
vda is a virtio-blk disk with PCI device 0000:00:06.0,
guest-get-fsinfo command will return the following result:
{"return":
[{"name":"dm-1",
"mountpoint":"/mnt/test",
"disk":[
{"bus-type":"scsi","bus":0,"unit":1,"target":0,
"pci-controller":{"bus":0,"slot":10,"domain":0,"function":0}},
{"bus-type":"ide","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":1,"domain":0,"function":1}}],
"type":"xfs"},
{"name":"vda1", "mountpoint":"/",
"disk":[
{"bus-type":"virtio","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":6,"domain":0,"function":0}}],
"type":"ext4"}]}
In Linux guest, the disk information is resolved from sysfs. So far,
it only supports virtio-blk, virtio-scsi, IDE, SATA, SCSI disks on x86
hosts, and "disk" parameter may be empty for unsupported disk types.
Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama@hds.com>
*updated schema to report 2.2 as initial supported version
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2014-07-01 05:51:34 +08:00
|
|
|
if (nhosts < 0) {
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pciaddr->domain = pci[0];
|
|
|
|
pciaddr->bus = pci[1];
|
|
|
|
pciaddr->slot = pci[2];
|
|
|
|
pciaddr->function = pci[3];
|
|
|
|
|
|
|
|
if (strcmp(driver, "ata_piix") == 0) {
|
|
|
|
/* a host per ide bus, target*:0:<unit>:0 */
|
|
|
|
if (!has_host || !has_tgt) {
|
|
|
|
g_debug("invalid sysfs path '%s' (driver '%s')", syspath, driver);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
for (i = 0; i < nhosts; i++) {
|
|
|
|
if (host == hosts[i]) {
|
|
|
|
disk->bus_type = GUEST_DISK_BUS_TYPE_IDE;
|
|
|
|
disk->bus = i;
|
|
|
|
disk->unit = tgt[1];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (i >= nhosts) {
|
|
|
|
g_debug("no host for '%s' (driver '%s')", syspath, driver);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
} else if (strcmp(driver, "sym53c8xx") == 0) {
|
|
|
|
/* scsi(LSI Logic): target*:0:<unit>:0 */
|
|
|
|
if (!has_tgt) {
|
|
|
|
g_debug("invalid sysfs path '%s' (driver '%s')", syspath, driver);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
disk->bus_type = GUEST_DISK_BUS_TYPE_SCSI;
|
|
|
|
disk->unit = tgt[1];
|
|
|
|
} else if (strcmp(driver, "virtio-pci") == 0) {
|
|
|
|
if (has_tgt) {
|
|
|
|
/* virtio-scsi: target*:0:0:<unit> */
|
|
|
|
disk->bus_type = GUEST_DISK_BUS_TYPE_SCSI;
|
|
|
|
disk->unit = tgt[2];
|
|
|
|
} else {
|
|
|
|
/* virtio-blk: 1 disk per 1 device */
|
|
|
|
disk->bus_type = GUEST_DISK_BUS_TYPE_VIRTIO;
|
|
|
|
}
|
|
|
|
} else if (strcmp(driver, "ahci") == 0) {
|
|
|
|
/* ahci: 1 host per 1 unit */
|
|
|
|
if (!has_host || !has_tgt) {
|
|
|
|
g_debug("invalid sysfs path '%s' (driver '%s')", syspath, driver);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
for (i = 0; i < nhosts; i++) {
|
|
|
|
if (host == hosts[i]) {
|
|
|
|
disk->unit = i;
|
|
|
|
disk->bus_type = GUEST_DISK_BUS_TYPE_SATA;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (i >= nhosts) {
|
|
|
|
g_debug("no host for '%s' (driver '%s')", syspath, driver);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
qga: Introduce NVMe disk bus type
Assigning a NVMe disk by VFIO or emulating a NVMe controller by QEMU,
a NVMe disk get exposed in guest side. Support NVMe disk bus type and
implement posix version.
Test PCI passthrough case:
~#virsh qemu-agent-command buster '{"execute":"guest-get-disks"}' | jq
...
{
"name": "/dev/nvme0n1",
"dependencies": [],
"partition": false,
"address": {
"serial": "SAMSUNG MZQL23T8HCLS-00A07_S64HNE0N500076",
"bus-type": "nvme",
"bus": 0,
"unit": 0,
"pci-controller": {
"bus": 0,
"slot": 22,
"domain": 0,
"function": 0
},
"dev": "/dev/nvme0n1",
"target": 0
}
...
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
Signed-off-by: zhenwei pi <pizhenwei@bytedance.com>
Message-Id: <20220420022610.418052-2-pizhenwei@bytedance.com>
2022-04-20 10:26:09 +08:00
|
|
|
} else if (strcmp(driver, "nvme") == 0) {
|
|
|
|
disk->bus_type = GUEST_DISK_BUS_TYPE_NVME;
|
2023-01-23 21:30:49 +08:00
|
|
|
} else if (strcmp(driver, "ehci-pci") == 0 || strcmp(driver, "xhci_hcd") == 0) {
|
|
|
|
disk->bus_type = GUEST_DISK_BUS_TYPE_USB;
|
qga: Add guest-get-fsinfo command
Add command to get mounted filesystems information in the guest.
The returned value contains a list of mountpoint paths and
corresponding disks info such as disk bus type, drive address,
and the disk controllers' PCI addresses, so that management layer
such as libvirt can resolve the disk backends.
For example, when `lsblk' result is:
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sdb 8:16 0 1G 0 disk
`-sdb1 8:17 0 1024M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
sdc 8:32 0 1G 0 disk
`-sdc1 8:33 0 512M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
vda 252:0 0 25G 0 disk
`-vda1 252:1 0 25G 0 part /
where sdb is a SCSI disk with PCI controller 0000:00:0a.0 and ID=1,
sdc is an IDE disk with PCI controller 0000:00:01.1, and
vda is a virtio-blk disk with PCI device 0000:00:06.0,
guest-get-fsinfo command will return the following result:
{"return":
[{"name":"dm-1",
"mountpoint":"/mnt/test",
"disk":[
{"bus-type":"scsi","bus":0,"unit":1,"target":0,
"pci-controller":{"bus":0,"slot":10,"domain":0,"function":0}},
{"bus-type":"ide","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":1,"domain":0,"function":1}}],
"type":"xfs"},
{"name":"vda1", "mountpoint":"/",
"disk":[
{"bus-type":"virtio","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":6,"domain":0,"function":0}}],
"type":"ext4"}]}
In Linux guest, the disk information is resolved from sysfs. So far,
it only supports virtio-blk, virtio-scsi, IDE, SATA, SCSI disks on x86
hosts, and "disk" parameter may be empty for unsupported disk types.
Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama@hds.com>
*updated schema to report 2.2 as initial supported version
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2014-07-01 05:51:34 +08:00
|
|
|
} else {
|
|
|
|
g_debug("unknown driver '%s' (sysfs path '%s')", driver, syspath);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2020-07-22 12:40:26 +08:00
|
|
|
ret = true;
|
qga: Add guest-get-fsinfo command
Add command to get mounted filesystems information in the guest.
The returned value contains a list of mountpoint paths and
corresponding disks info such as disk bus type, drive address,
and the disk controllers' PCI addresses, so that management layer
such as libvirt can resolve the disk backends.
For example, when `lsblk' result is:
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sdb 8:16 0 1G 0 disk
`-sdb1 8:17 0 1024M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
sdc 8:32 0 1G 0 disk
`-sdc1 8:33 0 512M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
vda 252:0 0 25G 0 disk
`-vda1 252:1 0 25G 0 part /
where sdb is a SCSI disk with PCI controller 0000:00:0a.0 and ID=1,
sdc is an IDE disk with PCI controller 0000:00:01.1, and
vda is a virtio-blk disk with PCI device 0000:00:06.0,
guest-get-fsinfo command will return the following result:
{"return":
[{"name":"dm-1",
"mountpoint":"/mnt/test",
"disk":[
{"bus-type":"scsi","bus":0,"unit":1,"target":0,
"pci-controller":{"bus":0,"slot":10,"domain":0,"function":0}},
{"bus-type":"ide","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":1,"domain":0,"function":1}}],
"type":"xfs"},
{"name":"vda1", "mountpoint":"/",
"disk":[
{"bus-type":"virtio","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":6,"domain":0,"function":0}}],
"type":"ext4"}]}
In Linux guest, the disk information is resolved from sysfs. So far,
it only supports virtio-blk, virtio-scsi, IDE, SATA, SCSI disks on x86
hosts, and "disk" parameter may be empty for unsupported disk types.
Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama@hds.com>
*updated schema to report 2.2 as initial supported version
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2014-07-01 05:51:34 +08:00
|
|
|
|
|
|
|
cleanup:
|
|
|
|
g_free(driver);
|
2020-07-22 12:40:26 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2020-07-22 12:40:28 +08:00
|
|
|
/*
|
|
|
|
* 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 = strstr(syspath, "/target");
|
|
|
|
if (p && sscanf(p + 7, "%*u:%*u:%*u/%*u:%u:%u:%u",
|
|
|
|
&tgt[0], &tgt[1], &tgt[2]) == 3) {
|
|
|
|
/* virtio-scsi: target*:0:<target>:<unit> */
|
|
|
|
disk->bus_type = GUEST_DISK_BUS_TYPE_SCSI;
|
|
|
|
disk->bus = tgt[0];
|
|
|
|
disk->target = tgt[1];
|
|
|
|
disk->unit = tgt[2];
|
|
|
|
} else {
|
|
|
|
/* virtio-blk: 1 disk per 1 device */
|
|
|
|
disk->bus_type = GUEST_DISK_BUS_TYPE_VIRTIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-11-27 16:23:53 +08:00
|
|
|
/*
|
|
|
|
* 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 = 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->ccw_address = g_new0(GuestCCWAddress, 1);
|
|
|
|
disk->ccw_address->cssid = cssid;
|
|
|
|
disk->ccw_address->ssid = ssid;
|
|
|
|
disk->ccw_address->subchno = subchno;
|
|
|
|
disk->ccw_address->devno = devno;
|
|
|
|
|
|
|
|
if (strstr(p, "/virtio")) {
|
|
|
|
build_guest_fsinfo_for_nonpci_virtio(syspath, disk, errp);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-07-22 12:40:26 +08:00
|
|
|
/* 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;
|
2020-07-22 12:40:27 +08:00
|
|
|
#ifdef CONFIG_LIBUDEV
|
|
|
|
struct udev *udev = NULL;
|
|
|
|
struct udev_device *udevice = NULL;
|
|
|
|
#endif
|
2020-07-22 12:40:26 +08:00
|
|
|
|
|
|
|
pciaddr = g_new0(GuestPCIAddress, 1);
|
2020-07-22 12:40:27 +08:00
|
|
|
pciaddr->domain = -1; /* -1 means field is invalid */
|
|
|
|
pciaddr->bus = -1;
|
|
|
|
pciaddr->slot = -1;
|
|
|
|
pciaddr->function = -1;
|
2020-07-22 12:40:26 +08:00
|
|
|
|
|
|
|
disk = g_new0(GuestDiskAddress, 1);
|
|
|
|
disk->pci_controller = pciaddr;
|
2020-07-22 12:40:27 +08:00
|
|
|
disk->bus_type = GUEST_DISK_BUS_TYPE_UNKNOWN;
|
2020-07-22 12:40:26 +08:00
|
|
|
|
2020-07-22 12:40:27 +08:00
|
|
|
#ifdef CONFIG_LIBUDEV
|
|
|
|
udev = udev_new();
|
|
|
|
udevice = udev_device_new_from_syspath(udev, syspath);
|
|
|
|
if (udev == NULL || udevice == NULL) {
|
|
|
|
g_debug("failed to query udev");
|
|
|
|
} else {
|
|
|
|
const char *devnode, *serial;
|
|
|
|
devnode = udev_device_get_devnode(udevice);
|
|
|
|
if (devnode != NULL) {
|
|
|
|
disk->dev = g_strdup(devnode);
|
|
|
|
}
|
|
|
|
serial = udev_device_get_property_value(udevice, "ID_SERIAL");
|
|
|
|
if (serial != NULL && *serial != 0) {
|
|
|
|
disk->serial = g_strdup(serial);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
udev_unref(udev);
|
|
|
|
udev_device_unref(udevice);
|
|
|
|
#endif
|
|
|
|
|
2020-07-22 12:40:28 +08:00
|
|
|
if (strstr(syspath, "/devices/pci")) {
|
|
|
|
has_hwinf = build_guest_fsinfo_for_pci_dev(syspath, disk, errp);
|
2020-11-27 16:23:53 +08:00
|
|
|
} else if (strstr(syspath, "/devices/css")) {
|
|
|
|
has_hwinf = build_guest_fsinfo_for_ccw_dev(syspath, disk, errp);
|
2020-07-22 12:40:28 +08:00
|
|
|
} else if (strstr(syspath, "/virtio")) {
|
|
|
|
has_hwinf = build_guest_fsinfo_for_nonpci_virtio(syspath, disk, errp);
|
|
|
|
} else {
|
|
|
|
g_debug("Unsupported device type for '%s'", syspath);
|
|
|
|
has_hwinf = false;
|
|
|
|
}
|
2020-07-22 12:40:26 +08:00
|
|
|
|
2022-11-05 00:07:11 +08:00
|
|
|
if (has_hwinf || disk->dev || disk->serial) {
|
2020-11-13 09:13:37 +08:00
|
|
|
QAPI_LIST_PREPEND(fs->disk, disk);
|
2020-07-22 12:40:26 +08:00
|
|
|
} else {
|
2020-11-13 09:13:37 +08:00
|
|
|
qapi_free_GuestDiskAddress(disk);
|
2020-07-22 12:40:26 +08:00
|
|
|
}
|
qga: Add guest-get-fsinfo command
Add command to get mounted filesystems information in the guest.
The returned value contains a list of mountpoint paths and
corresponding disks info such as disk bus type, drive address,
and the disk controllers' PCI addresses, so that management layer
such as libvirt can resolve the disk backends.
For example, when `lsblk' result is:
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sdb 8:16 0 1G 0 disk
`-sdb1 8:17 0 1024M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
sdc 8:32 0 1G 0 disk
`-sdc1 8:33 0 512M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
vda 252:0 0 25G 0 disk
`-vda1 252:1 0 25G 0 part /
where sdb is a SCSI disk with PCI controller 0000:00:0a.0 and ID=1,
sdc is an IDE disk with PCI controller 0000:00:01.1, and
vda is a virtio-blk disk with PCI device 0000:00:06.0,
guest-get-fsinfo command will return the following result:
{"return":
[{"name":"dm-1",
"mountpoint":"/mnt/test",
"disk":[
{"bus-type":"scsi","bus":0,"unit":1,"target":0,
"pci-controller":{"bus":0,"slot":10,"domain":0,"function":0}},
{"bus-type":"ide","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":1,"domain":0,"function":1}}],
"type":"xfs"},
{"name":"vda1", "mountpoint":"/",
"disk":[
{"bus-type":"virtio","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":6,"domain":0,"function":0}}],
"type":"ext4"}]}
In Linux guest, the disk information is resolved from sysfs. So far,
it only supports virtio-blk, virtio-scsi, IDE, SATA, SCSI disks on x86
hosts, and "disk" parameter may be empty for unsupported disk types.
Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama@hds.com>
*updated schema to report 2.2 as initial supported version
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2014-07-01 05:51:34 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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 into
|
|
|
|
* @fs */
|
|
|
|
static void build_guest_fsinfo_for_virtual_device(char const *syspath,
|
|
|
|
GuestFilesystemInfo *fs,
|
|
|
|
Error **errp)
|
|
|
|
{
|
2019-12-04 17:36:16 +08:00
|
|
|
Error *err = NULL;
|
qga: Add guest-get-fsinfo command
Add command to get mounted filesystems information in the guest.
The returned value contains a list of mountpoint paths and
corresponding disks info such as disk bus type, drive address,
and the disk controllers' PCI addresses, so that management layer
such as libvirt can resolve the disk backends.
For example, when `lsblk' result is:
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sdb 8:16 0 1G 0 disk
`-sdb1 8:17 0 1024M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
sdc 8:32 0 1G 0 disk
`-sdc1 8:33 0 512M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
vda 252:0 0 25G 0 disk
`-vda1 252:1 0 25G 0 part /
where sdb is a SCSI disk with PCI controller 0000:00:0a.0 and ID=1,
sdc is an IDE disk with PCI controller 0000:00:01.1, and
vda is a virtio-blk disk with PCI device 0000:00:06.0,
guest-get-fsinfo command will return the following result:
{"return":
[{"name":"dm-1",
"mountpoint":"/mnt/test",
"disk":[
{"bus-type":"scsi","bus":0,"unit":1,"target":0,
"pci-controller":{"bus":0,"slot":10,"domain":0,"function":0}},
{"bus-type":"ide","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":1,"domain":0,"function":1}}],
"type":"xfs"},
{"name":"vda1", "mountpoint":"/",
"disk":[
{"bus-type":"virtio","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":6,"domain":0,"function":0}}],
"type":"ext4"}]}
In Linux guest, the disk information is resolved from sysfs. So far,
it only supports virtio-blk, virtio-scsi, IDE, SATA, SCSI disks on x86
hosts, and "disk" parameter may be empty for unsupported disk types.
Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama@hds.com>
*updated schema to report 2.2 as initial supported version
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2014-07-01 05:51:34 +08:00
|
|
|
DIR *dir;
|
|
|
|
char *dirpath;
|
2014-09-19 11:09:10 +08:00
|
|
|
struct dirent *entry;
|
qga: Add guest-get-fsinfo command
Add command to get mounted filesystems information in the guest.
The returned value contains a list of mountpoint paths and
corresponding disks info such as disk bus type, drive address,
and the disk controllers' PCI addresses, so that management layer
such as libvirt can resolve the disk backends.
For example, when `lsblk' result is:
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sdb 8:16 0 1G 0 disk
`-sdb1 8:17 0 1024M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
sdc 8:32 0 1G 0 disk
`-sdc1 8:33 0 512M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
vda 252:0 0 25G 0 disk
`-vda1 252:1 0 25G 0 part /
where sdb is a SCSI disk with PCI controller 0000:00:0a.0 and ID=1,
sdc is an IDE disk with PCI controller 0000:00:01.1, and
vda is a virtio-blk disk with PCI device 0000:00:06.0,
guest-get-fsinfo command will return the following result:
{"return":
[{"name":"dm-1",
"mountpoint":"/mnt/test",
"disk":[
{"bus-type":"scsi","bus":0,"unit":1,"target":0,
"pci-controller":{"bus":0,"slot":10,"domain":0,"function":0}},
{"bus-type":"ide","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":1,"domain":0,"function":1}}],
"type":"xfs"},
{"name":"vda1", "mountpoint":"/",
"disk":[
{"bus-type":"virtio","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":6,"domain":0,"function":0}}],
"type":"ext4"}]}
In Linux guest, the disk information is resolved from sysfs. So far,
it only supports virtio-blk, virtio-scsi, IDE, SATA, SCSI disks on x86
hosts, and "disk" parameter may be empty for unsupported disk types.
Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama@hds.com>
*updated schema to report 2.2 as initial supported version
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2014-07-01 05:51:34 +08:00
|
|
|
|
|
|
|
dirpath = g_strdup_printf("%s/slaves", syspath);
|
|
|
|
dir = opendir(dirpath);
|
|
|
|
if (!dir) {
|
2017-03-24 04:24:32 +08:00
|
|
|
if (errno != ENOENT) {
|
|
|
|
error_setg_errno(errp, errno, "opendir(\"%s\")", dirpath);
|
|
|
|
}
|
qga: Add guest-get-fsinfo command
Add command to get mounted filesystems information in the guest.
The returned value contains a list of mountpoint paths and
corresponding disks info such as disk bus type, drive address,
and the disk controllers' PCI addresses, so that management layer
such as libvirt can resolve the disk backends.
For example, when `lsblk' result is:
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sdb 8:16 0 1G 0 disk
`-sdb1 8:17 0 1024M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
sdc 8:32 0 1G 0 disk
`-sdc1 8:33 0 512M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
vda 252:0 0 25G 0 disk
`-vda1 252:1 0 25G 0 part /
where sdb is a SCSI disk with PCI controller 0000:00:0a.0 and ID=1,
sdc is an IDE disk with PCI controller 0000:00:01.1, and
vda is a virtio-blk disk with PCI device 0000:00:06.0,
guest-get-fsinfo command will return the following result:
{"return":
[{"name":"dm-1",
"mountpoint":"/mnt/test",
"disk":[
{"bus-type":"scsi","bus":0,"unit":1,"target":0,
"pci-controller":{"bus":0,"slot":10,"domain":0,"function":0}},
{"bus-type":"ide","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":1,"domain":0,"function":1}}],
"type":"xfs"},
{"name":"vda1", "mountpoint":"/",
"disk":[
{"bus-type":"virtio","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":6,"domain":0,"function":0}}],
"type":"ext4"}]}
In Linux guest, the disk information is resolved from sysfs. So far,
it only supports virtio-blk, virtio-scsi, IDE, SATA, SCSI disks on x86
hosts, and "disk" parameter may be empty for unsupported disk types.
Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama@hds.com>
*updated schema to report 2.2 as initial supported version
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2014-07-01 05:51:34 +08:00
|
|
|
g_free(dirpath);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (;;) {
|
2014-09-19 11:09:10 +08:00
|
|
|
errno = 0;
|
|
|
|
entry = readdir(dir);
|
|
|
|
if (entry == NULL) {
|
|
|
|
if (errno) {
|
|
|
|
error_setg_errno(errp, errno, "readdir(\"%s\")", dirpath);
|
|
|
|
}
|
qga: Add guest-get-fsinfo command
Add command to get mounted filesystems information in the guest.
The returned value contains a list of mountpoint paths and
corresponding disks info such as disk bus type, drive address,
and the disk controllers' PCI addresses, so that management layer
such as libvirt can resolve the disk backends.
For example, when `lsblk' result is:
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sdb 8:16 0 1G 0 disk
`-sdb1 8:17 0 1024M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
sdc 8:32 0 1G 0 disk
`-sdc1 8:33 0 512M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
vda 252:0 0 25G 0 disk
`-vda1 252:1 0 25G 0 part /
where sdb is a SCSI disk with PCI controller 0000:00:0a.0 and ID=1,
sdc is an IDE disk with PCI controller 0000:00:01.1, and
vda is a virtio-blk disk with PCI device 0000:00:06.0,
guest-get-fsinfo command will return the following result:
{"return":
[{"name":"dm-1",
"mountpoint":"/mnt/test",
"disk":[
{"bus-type":"scsi","bus":0,"unit":1,"target":0,
"pci-controller":{"bus":0,"slot":10,"domain":0,"function":0}},
{"bus-type":"ide","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":1,"domain":0,"function":1}}],
"type":"xfs"},
{"name":"vda1", "mountpoint":"/",
"disk":[
{"bus-type":"virtio","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":6,"domain":0,"function":0}}],
"type":"ext4"}]}
In Linux guest, the disk information is resolved from sysfs. So far,
it only supports virtio-blk, virtio-scsi, IDE, SATA, SCSI disks on x86
hosts, and "disk" parameter may be empty for unsupported disk types.
Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama@hds.com>
*updated schema to report 2.2 as initial supported version
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2014-07-01 05:51:34 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2014-09-19 11:09:10 +08:00
|
|
|
if (entry->d_type == DT_LNK) {
|
|
|
|
char *path;
|
|
|
|
|
|
|
|
g_debug(" slave device '%s'", entry->d_name);
|
|
|
|
path = g_strdup_printf("%s/slaves/%s", syspath, entry->d_name);
|
2019-12-04 17:36:16 +08:00
|
|
|
build_guest_fsinfo_for_device(path, fs, &err);
|
2014-09-19 11:09:10 +08:00
|
|
|
g_free(path);
|
qga: Add guest-get-fsinfo command
Add command to get mounted filesystems information in the guest.
The returned value contains a list of mountpoint paths and
corresponding disks info such as disk bus type, drive address,
and the disk controllers' PCI addresses, so that management layer
such as libvirt can resolve the disk backends.
For example, when `lsblk' result is:
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sdb 8:16 0 1G 0 disk
`-sdb1 8:17 0 1024M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
sdc 8:32 0 1G 0 disk
`-sdc1 8:33 0 512M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
vda 252:0 0 25G 0 disk
`-vda1 252:1 0 25G 0 part /
where sdb is a SCSI disk with PCI controller 0000:00:0a.0 and ID=1,
sdc is an IDE disk with PCI controller 0000:00:01.1, and
vda is a virtio-blk disk with PCI device 0000:00:06.0,
guest-get-fsinfo command will return the following result:
{"return":
[{"name":"dm-1",
"mountpoint":"/mnt/test",
"disk":[
{"bus-type":"scsi","bus":0,"unit":1,"target":0,
"pci-controller":{"bus":0,"slot":10,"domain":0,"function":0}},
{"bus-type":"ide","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":1,"domain":0,"function":1}}],
"type":"xfs"},
{"name":"vda1", "mountpoint":"/",
"disk":[
{"bus-type":"virtio","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":6,"domain":0,"function":0}}],
"type":"ext4"}]}
In Linux guest, the disk information is resolved from sysfs. So far,
it only supports virtio-blk, virtio-scsi, IDE, SATA, SCSI disks on x86
hosts, and "disk" parameter may be empty for unsupported disk types.
Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama@hds.com>
*updated schema to report 2.2 as initial supported version
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2014-07-01 05:51:34 +08:00
|
|
|
|
2019-12-04 17:36:16 +08:00
|
|
|
if (err) {
|
|
|
|
error_propagate(errp, err);
|
qga: Add guest-get-fsinfo command
Add command to get mounted filesystems information in the guest.
The returned value contains a list of mountpoint paths and
corresponding disks info such as disk bus type, drive address,
and the disk controllers' PCI addresses, so that management layer
such as libvirt can resolve the disk backends.
For example, when `lsblk' result is:
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sdb 8:16 0 1G 0 disk
`-sdb1 8:17 0 1024M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
sdc 8:32 0 1G 0 disk
`-sdc1 8:33 0 512M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
vda 252:0 0 25G 0 disk
`-vda1 252:1 0 25G 0 part /
where sdb is a SCSI disk with PCI controller 0000:00:0a.0 and ID=1,
sdc is an IDE disk with PCI controller 0000:00:01.1, and
vda is a virtio-blk disk with PCI device 0000:00:06.0,
guest-get-fsinfo command will return the following result:
{"return":
[{"name":"dm-1",
"mountpoint":"/mnt/test",
"disk":[
{"bus-type":"scsi","bus":0,"unit":1,"target":0,
"pci-controller":{"bus":0,"slot":10,"domain":0,"function":0}},
{"bus-type":"ide","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":1,"domain":0,"function":1}}],
"type":"xfs"},
{"name":"vda1", "mountpoint":"/",
"disk":[
{"bus-type":"virtio","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":6,"domain":0,"function":0}}],
"type":"ext4"}]}
In Linux guest, the disk information is resolved from sysfs. So far,
it only supports virtio-blk, virtio-scsi, IDE, SATA, SCSI disks on x86
hosts, and "disk" parameter may be empty for unsupported disk types.
Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama@hds.com>
*updated schema to report 2.2 as initial supported version
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2014-07-01 05:51:34 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-19 11:09:10 +08:00
|
|
|
g_free(dirpath);
|
qga: Add guest-get-fsinfo command
Add command to get mounted filesystems information in the guest.
The returned value contains a list of mountpoint paths and
corresponding disks info such as disk bus type, drive address,
and the disk controllers' PCI addresses, so that management layer
such as libvirt can resolve the disk backends.
For example, when `lsblk' result is:
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sdb 8:16 0 1G 0 disk
`-sdb1 8:17 0 1024M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
sdc 8:32 0 1G 0 disk
`-sdc1 8:33 0 512M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
vda 252:0 0 25G 0 disk
`-vda1 252:1 0 25G 0 part /
where sdb is a SCSI disk with PCI controller 0000:00:0a.0 and ID=1,
sdc is an IDE disk with PCI controller 0000:00:01.1, and
vda is a virtio-blk disk with PCI device 0000:00:06.0,
guest-get-fsinfo command will return the following result:
{"return":
[{"name":"dm-1",
"mountpoint":"/mnt/test",
"disk":[
{"bus-type":"scsi","bus":0,"unit":1,"target":0,
"pci-controller":{"bus":0,"slot":10,"domain":0,"function":0}},
{"bus-type":"ide","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":1,"domain":0,"function":1}}],
"type":"xfs"},
{"name":"vda1", "mountpoint":"/",
"disk":[
{"bus-type":"virtio","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":6,"domain":0,"function":0}}],
"type":"ext4"}]}
In Linux guest, the disk information is resolved from sysfs. So far,
it only supports virtio-blk, virtio-scsi, IDE, SATA, SCSI disks on x86
hosts, and "disk" parameter may be empty for unsupported disk types.
Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama@hds.com>
*updated schema to report 2.2 as initial supported version
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2014-07-01 05:51:34 +08:00
|
|
|
closedir(dir);
|
|
|
|
}
|
|
|
|
|
qga: add implementation of guest-get-disks for Linux
The command lists all disks (real and virtual) as well as disk
partitions. For each disk the list of dependent disks is also listed and
/dev path is used as a handle so it can be matched with "name" field of
other returned disk entries. For disk partitions the "dependents" list
is populated with the the parent device for easier tracking of
hierarchy.
Example output:
{
"return": [
...
{
"name": "/dev/dm-0",
"partition": false,
"dependents": [
"/dev/sda2"
],
"alias": "luks-7062202e-5b9b-433e-81e8-6628c40da9f7"
},
{
"name": "/dev/sda2",
"partition": true,
"dependents": [
"/dev/sda"
]
},
{
"name": "/dev/sda",
"partition": false,
"address": {
"serial": "SAMSUNG_MZ7LN512HCHP-000L1_S1ZKNXAG822493",
"bus-type": "sata",
...
"dev": "/dev/sda",
"target": 0
},
"dependents": []
},
...
]
}
Signed-off-by: Tomáš Golembiovský <tgolembi@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
*add missing stub for !defined(CONFIG_FSFREEZE)
*remove unused deps_dir variable
Signed-off-by: Michael Roth <michael.roth@amd.com>
2020-10-12 16:36:02 +08:00
|
|
|
static bool is_disk_virtual(const char *devpath, Error **errp)
|
|
|
|
{
|
|
|
|
g_autofree char *syspath = realpath(devpath, NULL);
|
|
|
|
|
|
|
|
if (!syspath) {
|
|
|
|
error_setg_errno(errp, errno, "realpath(\"%s\")", devpath);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return strstr(syspath, "/devices/virtual/block/") != NULL;
|
|
|
|
}
|
|
|
|
|
qga: Add guest-get-fsinfo command
Add command to get mounted filesystems information in the guest.
The returned value contains a list of mountpoint paths and
corresponding disks info such as disk bus type, drive address,
and the disk controllers' PCI addresses, so that management layer
such as libvirt can resolve the disk backends.
For example, when `lsblk' result is:
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sdb 8:16 0 1G 0 disk
`-sdb1 8:17 0 1024M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
sdc 8:32 0 1G 0 disk
`-sdc1 8:33 0 512M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
vda 252:0 0 25G 0 disk
`-vda1 252:1 0 25G 0 part /
where sdb is a SCSI disk with PCI controller 0000:00:0a.0 and ID=1,
sdc is an IDE disk with PCI controller 0000:00:01.1, and
vda is a virtio-blk disk with PCI device 0000:00:06.0,
guest-get-fsinfo command will return the following result:
{"return":
[{"name":"dm-1",
"mountpoint":"/mnt/test",
"disk":[
{"bus-type":"scsi","bus":0,"unit":1,"target":0,
"pci-controller":{"bus":0,"slot":10,"domain":0,"function":0}},
{"bus-type":"ide","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":1,"domain":0,"function":1}}],
"type":"xfs"},
{"name":"vda1", "mountpoint":"/",
"disk":[
{"bus-type":"virtio","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":6,"domain":0,"function":0}}],
"type":"ext4"}]}
In Linux guest, the disk information is resolved from sysfs. So far,
it only supports virtio-blk, virtio-scsi, IDE, SATA, SCSI disks on x86
hosts, and "disk" parameter may be empty for unsupported disk types.
Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama@hds.com>
*updated schema to report 2.2 as initial supported version
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2014-07-01 05:51:34 +08:00
|
|
|
/* Dispatch to functions for virtual/real device */
|
|
|
|
static void build_guest_fsinfo_for_device(char const *devpath,
|
|
|
|
GuestFilesystemInfo *fs,
|
|
|
|
Error **errp)
|
|
|
|
{
|
qga: add implementation of guest-get-disks for Linux
The command lists all disks (real and virtual) as well as disk
partitions. For each disk the list of dependent disks is also listed and
/dev path is used as a handle so it can be matched with "name" field of
other returned disk entries. For disk partitions the "dependents" list
is populated with the the parent device for easier tracking of
hierarchy.
Example output:
{
"return": [
...
{
"name": "/dev/dm-0",
"partition": false,
"dependents": [
"/dev/sda2"
],
"alias": "luks-7062202e-5b9b-433e-81e8-6628c40da9f7"
},
{
"name": "/dev/sda2",
"partition": true,
"dependents": [
"/dev/sda"
]
},
{
"name": "/dev/sda",
"partition": false,
"address": {
"serial": "SAMSUNG_MZ7LN512HCHP-000L1_S1ZKNXAG822493",
"bus-type": "sata",
...
"dev": "/dev/sda",
"target": 0
},
"dependents": []
},
...
]
}
Signed-off-by: Tomáš Golembiovský <tgolembi@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
*add missing stub for !defined(CONFIG_FSFREEZE)
*remove unused deps_dir variable
Signed-off-by: Michael Roth <michael.roth@amd.com>
2020-10-12 16:36:02 +08:00
|
|
|
ERRP_GUARD();
|
|
|
|
g_autofree char *syspath = NULL;
|
|
|
|
bool is_virtual = false;
|
qga: Add guest-get-fsinfo command
Add command to get mounted filesystems information in the guest.
The returned value contains a list of mountpoint paths and
corresponding disks info such as disk bus type, drive address,
and the disk controllers' PCI addresses, so that management layer
such as libvirt can resolve the disk backends.
For example, when `lsblk' result is:
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sdb 8:16 0 1G 0 disk
`-sdb1 8:17 0 1024M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
sdc 8:32 0 1G 0 disk
`-sdc1 8:33 0 512M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
vda 252:0 0 25G 0 disk
`-vda1 252:1 0 25G 0 part /
where sdb is a SCSI disk with PCI controller 0000:00:0a.0 and ID=1,
sdc is an IDE disk with PCI controller 0000:00:01.1, and
vda is a virtio-blk disk with PCI device 0000:00:06.0,
guest-get-fsinfo command will return the following result:
{"return":
[{"name":"dm-1",
"mountpoint":"/mnt/test",
"disk":[
{"bus-type":"scsi","bus":0,"unit":1,"target":0,
"pci-controller":{"bus":0,"slot":10,"domain":0,"function":0}},
{"bus-type":"ide","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":1,"domain":0,"function":1}}],
"type":"xfs"},
{"name":"vda1", "mountpoint":"/",
"disk":[
{"bus-type":"virtio","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":6,"domain":0,"function":0}}],
"type":"ext4"}]}
In Linux guest, the disk information is resolved from sysfs. So far,
it only supports virtio-blk, virtio-scsi, IDE, SATA, SCSI disks on x86
hosts, and "disk" parameter may be empty for unsupported disk types.
Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama@hds.com>
*updated schema to report 2.2 as initial supported version
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2014-07-01 05:51:34 +08:00
|
|
|
|
qga: add implementation of guest-get-disks for Linux
The command lists all disks (real and virtual) as well as disk
partitions. For each disk the list of dependent disks is also listed and
/dev path is used as a handle so it can be matched with "name" field of
other returned disk entries. For disk partitions the "dependents" list
is populated with the the parent device for easier tracking of
hierarchy.
Example output:
{
"return": [
...
{
"name": "/dev/dm-0",
"partition": false,
"dependents": [
"/dev/sda2"
],
"alias": "luks-7062202e-5b9b-433e-81e8-6628c40da9f7"
},
{
"name": "/dev/sda2",
"partition": true,
"dependents": [
"/dev/sda"
]
},
{
"name": "/dev/sda",
"partition": false,
"address": {
"serial": "SAMSUNG_MZ7LN512HCHP-000L1_S1ZKNXAG822493",
"bus-type": "sata",
...
"dev": "/dev/sda",
"target": 0
},
"dependents": []
},
...
]
}
Signed-off-by: Tomáš Golembiovský <tgolembi@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
*add missing stub for !defined(CONFIG_FSFREEZE)
*remove unused deps_dir variable
Signed-off-by: Michael Roth <michael.roth@amd.com>
2020-10-12 16:36:02 +08:00
|
|
|
syspath = realpath(devpath, NULL);
|
qga: Add guest-get-fsinfo command
Add command to get mounted filesystems information in the guest.
The returned value contains a list of mountpoint paths and
corresponding disks info such as disk bus type, drive address,
and the disk controllers' PCI addresses, so that management layer
such as libvirt can resolve the disk backends.
For example, when `lsblk' result is:
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sdb 8:16 0 1G 0 disk
`-sdb1 8:17 0 1024M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
sdc 8:32 0 1G 0 disk
`-sdc1 8:33 0 512M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
vda 252:0 0 25G 0 disk
`-vda1 252:1 0 25G 0 part /
where sdb is a SCSI disk with PCI controller 0000:00:0a.0 and ID=1,
sdc is an IDE disk with PCI controller 0000:00:01.1, and
vda is a virtio-blk disk with PCI device 0000:00:06.0,
guest-get-fsinfo command will return the following result:
{"return":
[{"name":"dm-1",
"mountpoint":"/mnt/test",
"disk":[
{"bus-type":"scsi","bus":0,"unit":1,"target":0,
"pci-controller":{"bus":0,"slot":10,"domain":0,"function":0}},
{"bus-type":"ide","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":1,"domain":0,"function":1}}],
"type":"xfs"},
{"name":"vda1", "mountpoint":"/",
"disk":[
{"bus-type":"virtio","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":6,"domain":0,"function":0}}],
"type":"ext4"}]}
In Linux guest, the disk information is resolved from sysfs. So far,
it only supports virtio-blk, virtio-scsi, IDE, SATA, SCSI disks on x86
hosts, and "disk" parameter may be empty for unsupported disk types.
Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama@hds.com>
*updated schema to report 2.2 as initial supported version
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2014-07-01 05:51:34 +08:00
|
|
|
if (!syspath) {
|
2022-07-08 23:34:52 +08:00
|
|
|
if (errno != 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 = g_path_get_basename(devpath);
|
|
|
|
}
|
qga: Add guest-get-fsinfo command
Add command to get mounted filesystems information in the guest.
The returned value contains a list of mountpoint paths and
corresponding disks info such as disk bus type, drive address,
and the disk controllers' PCI addresses, so that management layer
such as libvirt can resolve the disk backends.
For example, when `lsblk' result is:
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sdb 8:16 0 1G 0 disk
`-sdb1 8:17 0 1024M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
sdc 8:32 0 1G 0 disk
`-sdc1 8:33 0 512M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
vda 252:0 0 25G 0 disk
`-vda1 252:1 0 25G 0 part /
where sdb is a SCSI disk with PCI controller 0000:00:0a.0 and ID=1,
sdc is an IDE disk with PCI controller 0000:00:01.1, and
vda is a virtio-blk disk with PCI device 0000:00:06.0,
guest-get-fsinfo command will return the following result:
{"return":
[{"name":"dm-1",
"mountpoint":"/mnt/test",
"disk":[
{"bus-type":"scsi","bus":0,"unit":1,"target":0,
"pci-controller":{"bus":0,"slot":10,"domain":0,"function":0}},
{"bus-type":"ide","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":1,"domain":0,"function":1}}],
"type":"xfs"},
{"name":"vda1", "mountpoint":"/",
"disk":[
{"bus-type":"virtio","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":6,"domain":0,"function":0}}],
"type":"ext4"}]}
In Linux guest, the disk information is resolved from sysfs. So far,
it only supports virtio-blk, virtio-scsi, IDE, SATA, SCSI disks on x86
hosts, and "disk" parameter may be empty for unsupported disk types.
Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama@hds.com>
*updated schema to report 2.2 as initial supported version
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2014-07-01 05:51:34 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!fs->name) {
|
2018-03-01 15:08:06 +08:00
|
|
|
fs->name = g_path_get_basename(syspath);
|
qga: Add guest-get-fsinfo command
Add command to get mounted filesystems information in the guest.
The returned value contains a list of mountpoint paths and
corresponding disks info such as disk bus type, drive address,
and the disk controllers' PCI addresses, so that management layer
such as libvirt can resolve the disk backends.
For example, when `lsblk' result is:
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sdb 8:16 0 1G 0 disk
`-sdb1 8:17 0 1024M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
sdc 8:32 0 1G 0 disk
`-sdc1 8:33 0 512M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
vda 252:0 0 25G 0 disk
`-vda1 252:1 0 25G 0 part /
where sdb is a SCSI disk with PCI controller 0000:00:0a.0 and ID=1,
sdc is an IDE disk with PCI controller 0000:00:01.1, and
vda is a virtio-blk disk with PCI device 0000:00:06.0,
guest-get-fsinfo command will return the following result:
{"return":
[{"name":"dm-1",
"mountpoint":"/mnt/test",
"disk":[
{"bus-type":"scsi","bus":0,"unit":1,"target":0,
"pci-controller":{"bus":0,"slot":10,"domain":0,"function":0}},
{"bus-type":"ide","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":1,"domain":0,"function":1}}],
"type":"xfs"},
{"name":"vda1", "mountpoint":"/",
"disk":[
{"bus-type":"virtio","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":6,"domain":0,"function":0}}],
"type":"ext4"}]}
In Linux guest, the disk information is resolved from sysfs. So far,
it only supports virtio-blk, virtio-scsi, IDE, SATA, SCSI disks on x86
hosts, and "disk" parameter may be empty for unsupported disk types.
Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama@hds.com>
*updated schema to report 2.2 as initial supported version
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2014-07-01 05:51:34 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
g_debug(" parse sysfs path '%s'", syspath);
|
qga: add implementation of guest-get-disks for Linux
The command lists all disks (real and virtual) as well as disk
partitions. For each disk the list of dependent disks is also listed and
/dev path is used as a handle so it can be matched with "name" field of
other returned disk entries. For disk partitions the "dependents" list
is populated with the the parent device for easier tracking of
hierarchy.
Example output:
{
"return": [
...
{
"name": "/dev/dm-0",
"partition": false,
"dependents": [
"/dev/sda2"
],
"alias": "luks-7062202e-5b9b-433e-81e8-6628c40da9f7"
},
{
"name": "/dev/sda2",
"partition": true,
"dependents": [
"/dev/sda"
]
},
{
"name": "/dev/sda",
"partition": false,
"address": {
"serial": "SAMSUNG_MZ7LN512HCHP-000L1_S1ZKNXAG822493",
"bus-type": "sata",
...
"dev": "/dev/sda",
"target": 0
},
"dependents": []
},
...
]
}
Signed-off-by: Tomáš Golembiovský <tgolembi@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
*add missing stub for !defined(CONFIG_FSFREEZE)
*remove unused deps_dir variable
Signed-off-by: Michael Roth <michael.roth@amd.com>
2020-10-12 16:36:02 +08:00
|
|
|
is_virtual = is_disk_virtual(syspath, errp);
|
|
|
|
if (*errp != NULL) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (is_virtual) {
|
qga: Add guest-get-fsinfo command
Add command to get mounted filesystems information in the guest.
The returned value contains a list of mountpoint paths and
corresponding disks info such as disk bus type, drive address,
and the disk controllers' PCI addresses, so that management layer
such as libvirt can resolve the disk backends.
For example, when `lsblk' result is:
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sdb 8:16 0 1G 0 disk
`-sdb1 8:17 0 1024M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
sdc 8:32 0 1G 0 disk
`-sdc1 8:33 0 512M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
vda 252:0 0 25G 0 disk
`-vda1 252:1 0 25G 0 part /
where sdb is a SCSI disk with PCI controller 0000:00:0a.0 and ID=1,
sdc is an IDE disk with PCI controller 0000:00:01.1, and
vda is a virtio-blk disk with PCI device 0000:00:06.0,
guest-get-fsinfo command will return the following result:
{"return":
[{"name":"dm-1",
"mountpoint":"/mnt/test",
"disk":[
{"bus-type":"scsi","bus":0,"unit":1,"target":0,
"pci-controller":{"bus":0,"slot":10,"domain":0,"function":0}},
{"bus-type":"ide","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":1,"domain":0,"function":1}}],
"type":"xfs"},
{"name":"vda1", "mountpoint":"/",
"disk":[
{"bus-type":"virtio","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":6,"domain":0,"function":0}}],
"type":"ext4"}]}
In Linux guest, the disk information is resolved from sysfs. So far,
it only supports virtio-blk, virtio-scsi, IDE, SATA, SCSI disks on x86
hosts, and "disk" parameter may be empty for unsupported disk types.
Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama@hds.com>
*updated schema to report 2.2 as initial supported version
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2014-07-01 05:51:34 +08:00
|
|
|
build_guest_fsinfo_for_virtual_device(syspath, fs, errp);
|
|
|
|
} else {
|
|
|
|
build_guest_fsinfo_for_real_device(syspath, fs, errp);
|
|
|
|
}
|
qga: add implementation of guest-get-disks for Linux
The command lists all disks (real and virtual) as well as disk
partitions. For each disk the list of dependent disks is also listed and
/dev path is used as a handle so it can be matched with "name" field of
other returned disk entries. For disk partitions the "dependents" list
is populated with the the parent device for easier tracking of
hierarchy.
Example output:
{
"return": [
...
{
"name": "/dev/dm-0",
"partition": false,
"dependents": [
"/dev/sda2"
],
"alias": "luks-7062202e-5b9b-433e-81e8-6628c40da9f7"
},
{
"name": "/dev/sda2",
"partition": true,
"dependents": [
"/dev/sda"
]
},
{
"name": "/dev/sda",
"partition": false,
"address": {
"serial": "SAMSUNG_MZ7LN512HCHP-000L1_S1ZKNXAG822493",
"bus-type": "sata",
...
"dev": "/dev/sda",
"target": 0
},
"dependents": []
},
...
]
}
Signed-off-by: Tomáš Golembiovský <tgolembi@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
*add missing stub for !defined(CONFIG_FSFREEZE)
*remove unused deps_dir variable
Signed-off-by: Michael Roth <michael.roth@amd.com>
2020-10-12 16:36:02 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#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 **errp)
|
|
|
|
{
|
|
|
|
g_autoptr(GuestFilesystemInfo) fs = NULL;
|
qga: Add guest-get-fsinfo command
Add command to get mounted filesystems information in the guest.
The returned value contains a list of mountpoint paths and
corresponding disks info such as disk bus type, drive address,
and the disk controllers' PCI addresses, so that management layer
such as libvirt can resolve the disk backends.
For example, when `lsblk' result is:
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sdb 8:16 0 1G 0 disk
`-sdb1 8:17 0 1024M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
sdc 8:32 0 1G 0 disk
`-sdc1 8:33 0 512M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
vda 252:0 0 25G 0 disk
`-vda1 252:1 0 25G 0 part /
where sdb is a SCSI disk with PCI controller 0000:00:0a.0 and ID=1,
sdc is an IDE disk with PCI controller 0000:00:01.1, and
vda is a virtio-blk disk with PCI device 0000:00:06.0,
guest-get-fsinfo command will return the following result:
{"return":
[{"name":"dm-1",
"mountpoint":"/mnt/test",
"disk":[
{"bus-type":"scsi","bus":0,"unit":1,"target":0,
"pci-controller":{"bus":0,"slot":10,"domain":0,"function":0}},
{"bus-type":"ide","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":1,"domain":0,"function":1}}],
"type":"xfs"},
{"name":"vda1", "mountpoint":"/",
"disk":[
{"bus-type":"virtio","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":6,"domain":0,"function":0}}],
"type":"ext4"}]}
In Linux guest, the disk information is resolved from sysfs. So far,
it only supports virtio-blk, virtio-scsi, IDE, SATA, SCSI disks on x86
hosts, and "disk" parameter may be empty for unsupported disk types.
Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama@hds.com>
*updated schema to report 2.2 as initial supported version
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2014-07-01 05:51:34 +08:00
|
|
|
|
qga: add implementation of guest-get-disks for Linux
The command lists all disks (real and virtual) as well as disk
partitions. For each disk the list of dependent disks is also listed and
/dev path is used as a handle so it can be matched with "name" field of
other returned disk entries. For disk partitions the "dependents" list
is populated with the the parent device for easier tracking of
hierarchy.
Example output:
{
"return": [
...
{
"name": "/dev/dm-0",
"partition": false,
"dependents": [
"/dev/sda2"
],
"alias": "luks-7062202e-5b9b-433e-81e8-6628c40da9f7"
},
{
"name": "/dev/sda2",
"partition": true,
"dependents": [
"/dev/sda"
]
},
{
"name": "/dev/sda",
"partition": false,
"address": {
"serial": "SAMSUNG_MZ7LN512HCHP-000L1_S1ZKNXAG822493",
"bus-type": "sata",
...
"dev": "/dev/sda",
"target": 0
},
"dependents": []
},
...
]
}
Signed-off-by: Tomáš Golembiovský <tgolembi@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
*add missing stub for !defined(CONFIG_FSFREEZE)
*remove unused deps_dir variable
Signed-off-by: Michael Roth <michael.roth@amd.com>
2020-10-12 16:36:02 +08:00
|
|
|
fs = g_new0(GuestFilesystemInfo, 1);
|
|
|
|
build_guest_fsinfo_for_device(syspath, fs, errp);
|
|
|
|
if (fs->disk != NULL) {
|
|
|
|
return g_steal_pointer(&fs->disk->value);
|
|
|
|
}
|
|
|
|
return NULL;
|
qga: Add guest-get-fsinfo command
Add command to get mounted filesystems information in the guest.
The returned value contains a list of mountpoint paths and
corresponding disks info such as disk bus type, drive address,
and the disk controllers' PCI addresses, so that management layer
such as libvirt can resolve the disk backends.
For example, when `lsblk' result is:
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sdb 8:16 0 1G 0 disk
`-sdb1 8:17 0 1024M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
sdc 8:32 0 1G 0 disk
`-sdc1 8:33 0 512M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
vda 252:0 0 25G 0 disk
`-vda1 252:1 0 25G 0 part /
where sdb is a SCSI disk with PCI controller 0000:00:0a.0 and ID=1,
sdc is an IDE disk with PCI controller 0000:00:01.1, and
vda is a virtio-blk disk with PCI device 0000:00:06.0,
guest-get-fsinfo command will return the following result:
{"return":
[{"name":"dm-1",
"mountpoint":"/mnt/test",
"disk":[
{"bus-type":"scsi","bus":0,"unit":1,"target":0,
"pci-controller":{"bus":0,"slot":10,"domain":0,"function":0}},
{"bus-type":"ide","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":1,"domain":0,"function":1}}],
"type":"xfs"},
{"name":"vda1", "mountpoint":"/",
"disk":[
{"bus-type":"virtio","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":6,"domain":0,"function":0}}],
"type":"ext4"}]}
In Linux guest, the disk information is resolved from sysfs. So far,
it only supports virtio-blk, virtio-scsi, IDE, SATA, SCSI disks on x86
hosts, and "disk" parameter may be empty for unsupported disk types.
Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama@hds.com>
*updated schema to report 2.2 as initial supported version
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2014-07-01 05:51:34 +08:00
|
|
|
}
|
|
|
|
|
qga: add implementation of guest-get-disks for Linux
The command lists all disks (real and virtual) as well as disk
partitions. For each disk the list of dependent disks is also listed and
/dev path is used as a handle so it can be matched with "name" field of
other returned disk entries. For disk partitions the "dependents" list
is populated with the the parent device for easier tracking of
hierarchy.
Example output:
{
"return": [
...
{
"name": "/dev/dm-0",
"partition": false,
"dependents": [
"/dev/sda2"
],
"alias": "luks-7062202e-5b9b-433e-81e8-6628c40da9f7"
},
{
"name": "/dev/sda2",
"partition": true,
"dependents": [
"/dev/sda"
]
},
{
"name": "/dev/sda",
"partition": false,
"address": {
"serial": "SAMSUNG_MZ7LN512HCHP-000L1_S1ZKNXAG822493",
"bus-type": "sata",
...
"dev": "/dev/sda",
"target": 0
},
"dependents": []
},
...
]
}
Signed-off-by: Tomáš Golembiovský <tgolembi@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
*add missing stub for !defined(CONFIG_FSFREEZE)
*remove unused deps_dir variable
Signed-off-by: Michael Roth <michael.roth@amd.com>
2020-10-12 16:36:02 +08:00
|
|
|
static char *get_alias_for_syspath(const char *syspath)
|
|
|
|
{
|
|
|
|
struct udev *udev = NULL;
|
|
|
|
struct udev_device *udevice = NULL;
|
|
|
|
char *ret = NULL;
|
|
|
|
|
|
|
|
udev = udev_new();
|
|
|
|
if (udev == NULL) {
|
|
|
|
g_debug("failed to query udev");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
udevice = udev_device_new_from_syspath(udev, syspath);
|
|
|
|
if (udevice == NULL) {
|
|
|
|
g_debug("failed to query udev for path: %s", syspath);
|
|
|
|
goto out;
|
|
|
|
} else {
|
|
|
|
const char *alias = 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 string.
|
|
|
|
*/
|
|
|
|
if (alias == NULL) {
|
|
|
|
g_debug("failed to query udev for device alias for: %s",
|
|
|
|
syspath);
|
|
|
|
} else if (*alias != 0) {
|
|
|
|
ret = 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 = NULL;
|
|
|
|
struct udev_device *udevice = NULL;
|
|
|
|
char *ret = NULL;
|
|
|
|
|
|
|
|
udev = udev_new();
|
|
|
|
if (udev == NULL) {
|
|
|
|
g_debug("failed to query udev");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
udevice = udev_device_new_from_syspath(udev, syspath);
|
|
|
|
if (udevice == NULL) {
|
|
|
|
g_debug("failed to query udev for path: %s", syspath);
|
|
|
|
goto out;
|
|
|
|
} else {
|
|
|
|
ret = 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 = NULL;
|
|
|
|
const gchar *dep;
|
|
|
|
GDir *dp_deps = NULL;
|
|
|
|
|
|
|
|
/* List dependent disks */
|
|
|
|
deps_dir = g_strdup_printf("%s/slaves", disk_dir);
|
|
|
|
g_debug(" listing entries in: %s", deps_dir);
|
|
|
|
dp_deps = g_dir_open(deps_dir, 0, NULL);
|
|
|
|
if (dp_deps == NULL) {
|
|
|
|
g_debug("failed to list entries in %s", deps_dir);
|
|
|
|
return;
|
|
|
|
}
|
2020-11-14 01:18:14 +08:00
|
|
|
disk->has_dependencies = true;
|
qga: add implementation of guest-get-disks for Linux
The command lists all disks (real and virtual) as well as disk
partitions. For each disk the list of dependent disks is also listed and
/dev path is used as a handle so it can be matched with "name" field of
other returned disk entries. For disk partitions the "dependents" list
is populated with the the parent device for easier tracking of
hierarchy.
Example output:
{
"return": [
...
{
"name": "/dev/dm-0",
"partition": false,
"dependents": [
"/dev/sda2"
],
"alias": "luks-7062202e-5b9b-433e-81e8-6628c40da9f7"
},
{
"name": "/dev/sda2",
"partition": true,
"dependents": [
"/dev/sda"
]
},
{
"name": "/dev/sda",
"partition": false,
"address": {
"serial": "SAMSUNG_MZ7LN512HCHP-000L1_S1ZKNXAG822493",
"bus-type": "sata",
...
"dev": "/dev/sda",
"target": 0
},
"dependents": []
},
...
]
}
Signed-off-by: Tomáš Golembiovský <tgolembi@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
*add missing stub for !defined(CONFIG_FSFREEZE)
*remove unused deps_dir variable
Signed-off-by: Michael Roth <michael.roth@amd.com>
2020-10-12 16:36:02 +08:00
|
|
|
while ((dep = g_dir_read_name(dp_deps)) != NULL) {
|
|
|
|
g_autofree char *dep_dir = NULL;
|
|
|
|
char *dev_name;
|
|
|
|
|
|
|
|
/* Add dependent disks */
|
|
|
|
dep_dir = g_strdup_printf("%s/%s", deps_dir, dep);
|
|
|
|
dev_name = get_device_for_syspath(dep_dir);
|
|
|
|
if (dev_name != NULL) {
|
|
|
|
g_debug(" adding dependent device: %s", dev_name);
|
2020-11-13 09:13:37 +08:00
|
|
|
QAPI_LIST_PREPEND(disk->dependencies, dev_name);
|
qga: add implementation of guest-get-disks for Linux
The command lists all disks (real and virtual) as well as disk
partitions. For each disk the list of dependent disks is also listed and
/dev path is used as a handle so it can be matched with "name" field of
other returned disk entries. For disk partitions the "dependents" list
is populated with the the parent device for easier tracking of
hierarchy.
Example output:
{
"return": [
...
{
"name": "/dev/dm-0",
"partition": false,
"dependents": [
"/dev/sda2"
],
"alias": "luks-7062202e-5b9b-433e-81e8-6628c40da9f7"
},
{
"name": "/dev/sda2",
"partition": true,
"dependents": [
"/dev/sda"
]
},
{
"name": "/dev/sda",
"partition": false,
"address": {
"serial": "SAMSUNG_MZ7LN512HCHP-000L1_S1ZKNXAG822493",
"bus-type": "sata",
...
"dev": "/dev/sda",
"target": 0
},
"dependents": []
},
...
]
}
Signed-off-by: Tomáš Golembiovský <tgolembi@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
*add missing stub for !defined(CONFIG_FSFREEZE)
*remove unused deps_dir variable
Signed-off-by: Michael Roth <michael.roth@amd.com>
2020-10-12 16:36:02 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
g_dir_close(dp_deps);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Detect partitions subdirectory, name is "<disk_name><number>" or
|
|
|
|
* "<disk_name>p<number>"
|
|
|
|
*
|
|
|
|
* @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)
|
|
|
|
{
|
2020-11-13 09:13:37 +08:00
|
|
|
GuestDiskInfoList *ret = list;
|
qga: add implementation of guest-get-disks for Linux
The command lists all disks (real and virtual) as well as disk
partitions. For each disk the list of dependent disks is also listed and
/dev path is used as a handle so it can be matched with "name" field of
other returned disk entries. For disk partitions the "dependents" list
is populated with the the parent device for easier tracking of
hierarchy.
Example output:
{
"return": [
...
{
"name": "/dev/dm-0",
"partition": false,
"dependents": [
"/dev/sda2"
],
"alias": "luks-7062202e-5b9b-433e-81e8-6628c40da9f7"
},
{
"name": "/dev/sda2",
"partition": true,
"dependents": [
"/dev/sda"
]
},
{
"name": "/dev/sda",
"partition": false,
"address": {
"serial": "SAMSUNG_MZ7LN512HCHP-000L1_S1ZKNXAG822493",
"bus-type": "sata",
...
"dev": "/dev/sda",
"target": 0
},
"dependents": []
},
...
]
}
Signed-off-by: Tomáš Golembiovský <tgolembi@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
*add missing stub for !defined(CONFIG_FSFREEZE)
*remove unused deps_dir variable
Signed-off-by: Michael Roth <michael.roth@amd.com>
2020-10-12 16:36:02 +08:00
|
|
|
struct dirent *de_disk;
|
|
|
|
DIR *dp_disk = NULL;
|
|
|
|
size_t len = strlen(disk_name);
|
|
|
|
|
|
|
|
dp_disk = opendir(disk_dir);
|
|
|
|
while ((de_disk = readdir(dp_disk)) != NULL) {
|
|
|
|
g_autofree char *partition_dir = NULL;
|
|
|
|
char *dev_name;
|
|
|
|
GuestDiskInfo *partition;
|
|
|
|
|
|
|
|
if (!(de_disk->d_type & DT_DIR)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(strncmp(disk_name, de_disk->d_name, len) == 0 &&
|
|
|
|
((*(de_disk->d_name + len) == 'p' &&
|
|
|
|
isdigit(*(de_disk->d_name + len + 1))) ||
|
|
|
|
isdigit(*(de_disk->d_name + len))))) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
partition_dir = g_strdup_printf("%s/%s",
|
|
|
|
disk_dir, de_disk->d_name);
|
|
|
|
dev_name = get_device_for_syspath(partition_dir);
|
|
|
|
if (dev_name == NULL) {
|
|
|
|
g_debug("Failed to get device name for syspath: %s",
|
|
|
|
disk_dir);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
partition = g_new0(GuestDiskInfo, 1);
|
|
|
|
partition->name = dev_name;
|
|
|
|
partition->partition = true;
|
2021-04-20 20:58:31 +08:00
|
|
|
partition->has_dependencies = true;
|
qga: add implementation of guest-get-disks for Linux
The command lists all disks (real and virtual) as well as disk
partitions. For each disk the list of dependent disks is also listed and
/dev path is used as a handle so it can be matched with "name" field of
other returned disk entries. For disk partitions the "dependents" list
is populated with the the parent device for easier tracking of
hierarchy.
Example output:
{
"return": [
...
{
"name": "/dev/dm-0",
"partition": false,
"dependents": [
"/dev/sda2"
],
"alias": "luks-7062202e-5b9b-433e-81e8-6628c40da9f7"
},
{
"name": "/dev/sda2",
"partition": true,
"dependents": [
"/dev/sda"
]
},
{
"name": "/dev/sda",
"partition": false,
"address": {
"serial": "SAMSUNG_MZ7LN512HCHP-000L1_S1ZKNXAG822493",
"bus-type": "sata",
...
"dev": "/dev/sda",
"target": 0
},
"dependents": []
},
...
]
}
Signed-off-by: Tomáš Golembiovský <tgolembi@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
*add missing stub for !defined(CONFIG_FSFREEZE)
*remove unused deps_dir variable
Signed-off-by: Michael Roth <michael.roth@amd.com>
2020-10-12 16:36:02 +08:00
|
|
|
/* Add parent disk as dependent for easier tracking of hierarchy */
|
2020-11-13 09:13:37 +08:00
|
|
|
QAPI_LIST_PREPEND(partition->dependencies, g_strdup(disk_dev));
|
qga: add implementation of guest-get-disks for Linux
The command lists all disks (real and virtual) as well as disk
partitions. For each disk the list of dependent disks is also listed and
/dev path is used as a handle so it can be matched with "name" field of
other returned disk entries. For disk partitions the "dependents" list
is populated with the the parent device for easier tracking of
hierarchy.
Example output:
{
"return": [
...
{
"name": "/dev/dm-0",
"partition": false,
"dependents": [
"/dev/sda2"
],
"alias": "luks-7062202e-5b9b-433e-81e8-6628c40da9f7"
},
{
"name": "/dev/sda2",
"partition": true,
"dependents": [
"/dev/sda"
]
},
{
"name": "/dev/sda",
"partition": false,
"address": {
"serial": "SAMSUNG_MZ7LN512HCHP-000L1_S1ZKNXAG822493",
"bus-type": "sata",
...
"dev": "/dev/sda",
"target": 0
},
"dependents": []
},
...
]
}
Signed-off-by: Tomáš Golembiovský <tgolembi@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
*add missing stub for !defined(CONFIG_FSFREEZE)
*remove unused deps_dir variable
Signed-off-by: Michael Roth <michael.roth@amd.com>
2020-10-12 16:36:02 +08:00
|
|
|
|
2020-11-13 09:13:37 +08:00
|
|
|
QAPI_LIST_PREPEND(ret, partition);
|
qga: add implementation of guest-get-disks for Linux
The command lists all disks (real and virtual) as well as disk
partitions. For each disk the list of dependent disks is also listed and
/dev path is used as a handle so it can be matched with "name" field of
other returned disk entries. For disk partitions the "dependents" list
is populated with the the parent device for easier tracking of
hierarchy.
Example output:
{
"return": [
...
{
"name": "/dev/dm-0",
"partition": false,
"dependents": [
"/dev/sda2"
],
"alias": "luks-7062202e-5b9b-433e-81e8-6628c40da9f7"
},
{
"name": "/dev/sda2",
"partition": true,
"dependents": [
"/dev/sda"
]
},
{
"name": "/dev/sda",
"partition": false,
"address": {
"serial": "SAMSUNG_MZ7LN512HCHP-000L1_S1ZKNXAG822493",
"bus-type": "sata",
...
"dev": "/dev/sda",
"target": 0
},
"dependents": []
},
...
]
}
Signed-off-by: Tomáš Golembiovský <tgolembi@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
*add missing stub for !defined(CONFIG_FSFREEZE)
*remove unused deps_dir variable
Signed-off-by: Michael Roth <michael.roth@amd.com>
2020-10-12 16:36:02 +08:00
|
|
|
}
|
|
|
|
closedir(dp_disk);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2022-04-20 10:26:10 +08:00
|
|
|
static void get_nvme_smart(GuestDiskInfo *disk)
|
|
|
|
{
|
|
|
|
int fd;
|
|
|
|
GuestNVMeSmart *smart;
|
|
|
|
NvmeSmartLog log = {0};
|
|
|
|
struct nvme_admin_cmd cmd = {
|
|
|
|
.opcode = NVME_ADM_CMD_GET_LOG_PAGE,
|
|
|
|
.nsid = NVME_NSID_BROADCAST,
|
|
|
|
.addr = (uintptr_t)&log,
|
|
|
|
.data_len = sizeof(log),
|
|
|
|
.cdw10 = NVME_LOG_SMART_INFO | (1 << 15) /* RAE bit */
|
|
|
|
| (((sizeof(log) >> 2) - 1) << 16)
|
|
|
|
};
|
|
|
|
|
2022-05-25 22:41:33 +08:00
|
|
|
fd = qga_open_cloexec(disk->name, O_RDONLY, 0);
|
2022-04-20 10:26:10 +08:00
|
|
|
if (fd == -1) {
|
|
|
|
g_debug("Failed to open device: %s: %s", disk->name, g_strerror(errno));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ioctl(fd, NVME_IOCTL_ADMIN_CMD, &cmd)) {
|
|
|
|
g_debug("Failed to get smart: %s: %s", disk->name, g_strerror(errno));
|
|
|
|
close(fd);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
disk->smart = g_new0(GuestDiskSmart, 1);
|
|
|
|
disk->smart->type = GUEST_DISK_BUS_TYPE_NVME;
|
|
|
|
|
|
|
|
smart = &disk->smart->u.nvme;
|
|
|
|
smart->critical_warning = log.critical_warning;
|
|
|
|
smart->temperature = lduw_le_p(&log.temperature); /* unaligned field */
|
|
|
|
smart->available_spare = log.available_spare;
|
|
|
|
smart->available_spare_threshold = log.available_spare_threshold;
|
|
|
|
smart->percentage_used = log.percentage_used;
|
|
|
|
smart->data_units_read_lo = le64_to_cpu(log.data_units_read[0]);
|
|
|
|
smart->data_units_read_hi = le64_to_cpu(log.data_units_read[1]);
|
|
|
|
smart->data_units_written_lo = le64_to_cpu(log.data_units_written[0]);
|
|
|
|
smart->data_units_written_hi = le64_to_cpu(log.data_units_written[1]);
|
|
|
|
smart->host_read_commands_lo = le64_to_cpu(log.host_read_commands[0]);
|
|
|
|
smart->host_read_commands_hi = le64_to_cpu(log.host_read_commands[1]);
|
|
|
|
smart->host_write_commands_lo = le64_to_cpu(log.host_write_commands[0]);
|
|
|
|
smart->host_write_commands_hi = le64_to_cpu(log.host_write_commands[1]);
|
|
|
|
smart->controller_busy_time_lo = le64_to_cpu(log.controller_busy_time[0]);
|
|
|
|
smart->controller_busy_time_hi = le64_to_cpu(log.controller_busy_time[1]);
|
|
|
|
smart->power_cycles_lo = le64_to_cpu(log.power_cycles[0]);
|
|
|
|
smart->power_cycles_hi = le64_to_cpu(log.power_cycles[1]);
|
|
|
|
smart->power_on_hours_lo = le64_to_cpu(log.power_on_hours[0]);
|
|
|
|
smart->power_on_hours_hi = le64_to_cpu(log.power_on_hours[1]);
|
|
|
|
smart->unsafe_shutdowns_lo = le64_to_cpu(log.unsafe_shutdowns[0]);
|
|
|
|
smart->unsafe_shutdowns_hi = le64_to_cpu(log.unsafe_shutdowns[1]);
|
|
|
|
smart->media_errors_lo = le64_to_cpu(log.media_errors[0]);
|
|
|
|
smart->media_errors_hi = le64_to_cpu(log.media_errors[1]);
|
|
|
|
smart->number_of_error_log_entries_lo =
|
|
|
|
le64_to_cpu(log.number_of_error_log_entries[0]);
|
|
|
|
smart->number_of_error_log_entries_hi =
|
|
|
|
le64_to_cpu(log.number_of_error_log_entries[1]);
|
|
|
|
|
|
|
|
close(fd);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void get_disk_smart(GuestDiskInfo *disk)
|
|
|
|
{
|
2022-11-05 00:07:11 +08:00
|
|
|
if (disk->address
|
2022-04-20 10:26:10 +08:00
|
|
|
&& (disk->address->bus_type == GUEST_DISK_BUS_TYPE_NVME)) {
|
|
|
|
get_nvme_smart(disk);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
qga: add implementation of guest-get-disks for Linux
The command lists all disks (real and virtual) as well as disk
partitions. For each disk the list of dependent disks is also listed and
/dev path is used as a handle so it can be matched with "name" field of
other returned disk entries. For disk partitions the "dependents" list
is populated with the the parent device for easier tracking of
hierarchy.
Example output:
{
"return": [
...
{
"name": "/dev/dm-0",
"partition": false,
"dependents": [
"/dev/sda2"
],
"alias": "luks-7062202e-5b9b-433e-81e8-6628c40da9f7"
},
{
"name": "/dev/sda2",
"partition": true,
"dependents": [
"/dev/sda"
]
},
{
"name": "/dev/sda",
"partition": false,
"address": {
"serial": "SAMSUNG_MZ7LN512HCHP-000L1_S1ZKNXAG822493",
"bus-type": "sata",
...
"dev": "/dev/sda",
"target": 0
},
"dependents": []
},
...
]
}
Signed-off-by: Tomáš Golembiovský <tgolembi@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
*add missing stub for !defined(CONFIG_FSFREEZE)
*remove unused deps_dir variable
Signed-off-by: Michael Roth <michael.roth@amd.com>
2020-10-12 16:36:02 +08:00
|
|
|
GuestDiskInfoList *qmp_guest_get_disks(Error **errp)
|
|
|
|
{
|
2020-11-13 09:13:37 +08:00
|
|
|
GuestDiskInfoList *ret = NULL;
|
qga: add implementation of guest-get-disks for Linux
The command lists all disks (real and virtual) as well as disk
partitions. For each disk the list of dependent disks is also listed and
/dev path is used as a handle so it can be matched with "name" field of
other returned disk entries. For disk partitions the "dependents" list
is populated with the the parent device for easier tracking of
hierarchy.
Example output:
{
"return": [
...
{
"name": "/dev/dm-0",
"partition": false,
"dependents": [
"/dev/sda2"
],
"alias": "luks-7062202e-5b9b-433e-81e8-6628c40da9f7"
},
{
"name": "/dev/sda2",
"partition": true,
"dependents": [
"/dev/sda"
]
},
{
"name": "/dev/sda",
"partition": false,
"address": {
"serial": "SAMSUNG_MZ7LN512HCHP-000L1_S1ZKNXAG822493",
"bus-type": "sata",
...
"dev": "/dev/sda",
"target": 0
},
"dependents": []
},
...
]
}
Signed-off-by: Tomáš Golembiovský <tgolembi@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
*add missing stub for !defined(CONFIG_FSFREEZE)
*remove unused deps_dir variable
Signed-off-by: Michael Roth <michael.roth@amd.com>
2020-10-12 16:36:02 +08:00
|
|
|
GuestDiskInfo *disk;
|
|
|
|
DIR *dp = NULL;
|
|
|
|
struct dirent *de = NULL;
|
|
|
|
|
|
|
|
g_debug("listing /sys/block directory");
|
|
|
|
dp = opendir("/sys/block");
|
|
|
|
if (dp == NULL) {
|
|
|
|
error_setg_errno(errp, errno, "Can't open directory \"/sys/block\"");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
while ((de = readdir(dp)) != NULL) {
|
|
|
|
g_autofree char *disk_dir = NULL, *line = NULL,
|
|
|
|
*size_path = NULL;
|
|
|
|
char *dev_name;
|
|
|
|
Error *local_err = NULL;
|
|
|
|
if (de->d_type != 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 = 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") == 0) {
|
|
|
|
g_debug(" skipping zero-sized disk");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
g_debug(" adding %s", de->d_name);
|
|
|
|
disk_dir = g_strdup_printf("/sys/block/%s", de->d_name);
|
|
|
|
dev_name = get_device_for_syspath(disk_dir);
|
|
|
|
if (dev_name == NULL) {
|
|
|
|
g_debug("Failed to get device name for syspath: %s",
|
|
|
|
disk_dir);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
disk = g_new0(GuestDiskInfo, 1);
|
|
|
|
disk->name = dev_name;
|
|
|
|
disk->partition = false;
|
|
|
|
disk->alias = get_alias_for_syspath(disk_dir);
|
2020-11-13 09:13:37 +08:00
|
|
|
QAPI_LIST_PREPEND(ret, disk);
|
qga: add implementation of guest-get-disks for Linux
The command lists all disks (real and virtual) as well as disk
partitions. For each disk the list of dependent disks is also listed and
/dev path is used as a handle so it can be matched with "name" field of
other returned disk entries. For disk partitions the "dependents" list
is populated with the the parent device for easier tracking of
hierarchy.
Example output:
{
"return": [
...
{
"name": "/dev/dm-0",
"partition": false,
"dependents": [
"/dev/sda2"
],
"alias": "luks-7062202e-5b9b-433e-81e8-6628c40da9f7"
},
{
"name": "/dev/sda2",
"partition": true,
"dependents": [
"/dev/sda"
]
},
{
"name": "/dev/sda",
"partition": false,
"address": {
"serial": "SAMSUNG_MZ7LN512HCHP-000L1_S1ZKNXAG822493",
"bus-type": "sata",
...
"dev": "/dev/sda",
"target": 0
},
"dependents": []
},
...
]
}
Signed-off-by: Tomáš Golembiovský <tgolembi@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
*add missing stub for !defined(CONFIG_FSFREEZE)
*remove unused deps_dir variable
Signed-off-by: Michael Roth <michael.roth@amd.com>
2020-10-12 16:36:02 +08:00
|
|
|
|
|
|
|
/* Get address for non-virtual devices */
|
|
|
|
bool is_virtual = is_disk_virtual(disk_dir, &local_err);
|
|
|
|
if (local_err != NULL) {
|
|
|
|
g_debug(" failed to check disk path, ignoring error: %s",
|
|
|
|
error_get_pretty(local_err));
|
|
|
|
error_free(local_err);
|
|
|
|
local_err = NULL;
|
|
|
|
/* Don't try to get the address */
|
|
|
|
is_virtual = true;
|
|
|
|
}
|
|
|
|
if (!is_virtual) {
|
|
|
|
disk->address = get_disk_address(disk_dir, &local_err);
|
|
|
|
if (local_err != NULL) {
|
|
|
|
g_debug(" failed to get device info, ignoring error: %s",
|
|
|
|
error_get_pretty(local_err));
|
|
|
|
error_free(local_err);
|
|
|
|
local_err = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
get_disk_deps(disk_dir, disk);
|
2022-04-20 10:26:10 +08:00
|
|
|
get_disk_smart(disk);
|
qga: add implementation of guest-get-disks for Linux
The command lists all disks (real and virtual) as well as disk
partitions. For each disk the list of dependent disks is also listed and
/dev path is used as a handle so it can be matched with "name" field of
other returned disk entries. For disk partitions the "dependents" list
is populated with the the parent device for easier tracking of
hierarchy.
Example output:
{
"return": [
...
{
"name": "/dev/dm-0",
"partition": false,
"dependents": [
"/dev/sda2"
],
"alias": "luks-7062202e-5b9b-433e-81e8-6628c40da9f7"
},
{
"name": "/dev/sda2",
"partition": true,
"dependents": [
"/dev/sda"
]
},
{
"name": "/dev/sda",
"partition": false,
"address": {
"serial": "SAMSUNG_MZ7LN512HCHP-000L1_S1ZKNXAG822493",
"bus-type": "sata",
...
"dev": "/dev/sda",
"target": 0
},
"dependents": []
},
...
]
}
Signed-off-by: Tomáš Golembiovský <tgolembi@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
*add missing stub for !defined(CONFIG_FSFREEZE)
*remove unused deps_dir variable
Signed-off-by: Michael Roth <michael.roth@amd.com>
2020-10-12 16:36:02 +08:00
|
|
|
ret = get_disk_partitions(ret, de->d_name, disk_dir, dev_name);
|
|
|
|
}
|
2020-11-08 22:37:41 +08:00
|
|
|
|
|
|
|
closedir(dp);
|
|
|
|
|
qga: add implementation of guest-get-disks for Linux
The command lists all disks (real and virtual) as well as disk
partitions. For each disk the list of dependent disks is also listed and
/dev path is used as a handle so it can be matched with "name" field of
other returned disk entries. For disk partitions the "dependents" list
is populated with the the parent device for easier tracking of
hierarchy.
Example output:
{
"return": [
...
{
"name": "/dev/dm-0",
"partition": false,
"dependents": [
"/dev/sda2"
],
"alias": "luks-7062202e-5b9b-433e-81e8-6628c40da9f7"
},
{
"name": "/dev/sda2",
"partition": true,
"dependents": [
"/dev/sda"
]
},
{
"name": "/dev/sda",
"partition": false,
"address": {
"serial": "SAMSUNG_MZ7LN512HCHP-000L1_S1ZKNXAG822493",
"bus-type": "sata",
...
"dev": "/dev/sda",
"target": 0
},
"dependents": []
},
...
]
}
Signed-off-by: Tomáš Golembiovský <tgolembi@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
*add missing stub for !defined(CONFIG_FSFREEZE)
*remove unused deps_dir variable
Signed-off-by: Michael Roth <michael.roth@amd.com>
2020-10-12 16:36:02 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
GuestDiskInfoList *qmp_guest_get_disks(Error **errp)
|
|
|
|
{
|
|
|
|
error_setg(errp, QERR_UNSUPPORTED);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
qga: Add guest-get-fsinfo command
Add command to get mounted filesystems information in the guest.
The returned value contains a list of mountpoint paths and
corresponding disks info such as disk bus type, drive address,
and the disk controllers' PCI addresses, so that management layer
such as libvirt can resolve the disk backends.
For example, when `lsblk' result is:
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sdb 8:16 0 1G 0 disk
`-sdb1 8:17 0 1024M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
sdc 8:32 0 1G 0 disk
`-sdc1 8:33 0 512M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
vda 252:0 0 25G 0 disk
`-vda1 252:1 0 25G 0 part /
where sdb is a SCSI disk with PCI controller 0000:00:0a.0 and ID=1,
sdc is an IDE disk with PCI controller 0000:00:01.1, and
vda is a virtio-blk disk with PCI device 0000:00:06.0,
guest-get-fsinfo command will return the following result:
{"return":
[{"name":"dm-1",
"mountpoint":"/mnt/test",
"disk":[
{"bus-type":"scsi","bus":0,"unit":1,"target":0,
"pci-controller":{"bus":0,"slot":10,"domain":0,"function":0}},
{"bus-type":"ide","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":1,"domain":0,"function":1}}],
"type":"xfs"},
{"name":"vda1", "mountpoint":"/",
"disk":[
{"bus-type":"virtio","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":6,"domain":0,"function":0}}],
"type":"ext4"}]}
In Linux guest, the disk information is resolved from sysfs. So far,
it only supports virtio-blk, virtio-scsi, IDE, SATA, SCSI disks on x86
hosts, and "disk" parameter may be empty for unsupported disk types.
Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama@hds.com>
*updated schema to report 2.2 as initial supported version
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2014-07-01 05:51:34 +08:00
|
|
|
/* 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 = g_malloc0(sizeof(*fs));
|
2018-06-14 16:06:06 +08:00
|
|
|
struct statvfs buf;
|
|
|
|
unsigned long used, nonroot_total, fr_size;
|
qga: Add guest-get-fsinfo command
Add command to get mounted filesystems information in the guest.
The returned value contains a list of mountpoint paths and
corresponding disks info such as disk bus type, drive address,
and the disk controllers' PCI addresses, so that management layer
such as libvirt can resolve the disk backends.
For example, when `lsblk' result is:
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sdb 8:16 0 1G 0 disk
`-sdb1 8:17 0 1024M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
sdc 8:32 0 1G 0 disk
`-sdc1 8:33 0 512M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
vda 252:0 0 25G 0 disk
`-vda1 252:1 0 25G 0 part /
where sdb is a SCSI disk with PCI controller 0000:00:0a.0 and ID=1,
sdc is an IDE disk with PCI controller 0000:00:01.1, and
vda is a virtio-blk disk with PCI device 0000:00:06.0,
guest-get-fsinfo command will return the following result:
{"return":
[{"name":"dm-1",
"mountpoint":"/mnt/test",
"disk":[
{"bus-type":"scsi","bus":0,"unit":1,"target":0,
"pci-controller":{"bus":0,"slot":10,"domain":0,"function":0}},
{"bus-type":"ide","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":1,"domain":0,"function":1}}],
"type":"xfs"},
{"name":"vda1", "mountpoint":"/",
"disk":[
{"bus-type":"virtio","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":6,"domain":0,"function":0}}],
"type":"ext4"}]}
In Linux guest, the disk information is resolved from sysfs. So far,
it only supports virtio-blk, virtio-scsi, IDE, SATA, SCSI disks on x86
hosts, and "disk" parameter may be empty for unsupported disk types.
Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama@hds.com>
*updated schema to report 2.2 as initial supported version
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2014-07-01 05:51:34 +08:00
|
|
|
char *devpath = g_strdup_printf("/sys/dev/block/%u:%u",
|
|
|
|
mount->devmajor, mount->devminor);
|
|
|
|
|
|
|
|
fs->mountpoint = g_strdup(mount->dirname);
|
|
|
|
fs->type = g_strdup(mount->devtype);
|
|
|
|
build_guest_fsinfo_for_device(devpath, fs, errp);
|
|
|
|
|
2018-06-14 16:06:06 +08:00
|
|
|
if (statvfs(fs->mountpoint, &buf) == 0) {
|
|
|
|
fr_size = buf.f_frsize;
|
|
|
|
used = buf.f_blocks - buf.f_bfree;
|
|
|
|
nonroot_total = used + buf.f_bavail;
|
|
|
|
fs->used_bytes = used * fr_size;
|
|
|
|
fs->total_bytes = nonroot_total * fr_size;
|
|
|
|
|
|
|
|
fs->has_total_bytes = true;
|
|
|
|
fs->has_used_bytes = true;
|
|
|
|
}
|
|
|
|
|
qga: Add guest-get-fsinfo command
Add command to get mounted filesystems information in the guest.
The returned value contains a list of mountpoint paths and
corresponding disks info such as disk bus type, drive address,
and the disk controllers' PCI addresses, so that management layer
such as libvirt can resolve the disk backends.
For example, when `lsblk' result is:
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sdb 8:16 0 1G 0 disk
`-sdb1 8:17 0 1024M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
sdc 8:32 0 1G 0 disk
`-sdc1 8:33 0 512M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
vda 252:0 0 25G 0 disk
`-vda1 252:1 0 25G 0 part /
where sdb is a SCSI disk with PCI controller 0000:00:0a.0 and ID=1,
sdc is an IDE disk with PCI controller 0000:00:01.1, and
vda is a virtio-blk disk with PCI device 0000:00:06.0,
guest-get-fsinfo command will return the following result:
{"return":
[{"name":"dm-1",
"mountpoint":"/mnt/test",
"disk":[
{"bus-type":"scsi","bus":0,"unit":1,"target":0,
"pci-controller":{"bus":0,"slot":10,"domain":0,"function":0}},
{"bus-type":"ide","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":1,"domain":0,"function":1}}],
"type":"xfs"},
{"name":"vda1", "mountpoint":"/",
"disk":[
{"bus-type":"virtio","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":6,"domain":0,"function":0}}],
"type":"ext4"}]}
In Linux guest, the disk information is resolved from sysfs. So far,
it only supports virtio-blk, virtio-scsi, IDE, SATA, SCSI disks on x86
hosts, and "disk" parameter may be empty for unsupported disk types.
Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama@hds.com>
*updated schema to report 2.2 as initial supported version
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2014-07-01 05:51:34 +08:00
|
|
|
g_free(devpath);
|
2018-06-14 16:06:06 +08:00
|
|
|
|
qga: Add guest-get-fsinfo command
Add command to get mounted filesystems information in the guest.
The returned value contains a list of mountpoint paths and
corresponding disks info such as disk bus type, drive address,
and the disk controllers' PCI addresses, so that management layer
such as libvirt can resolve the disk backends.
For example, when `lsblk' result is:
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sdb 8:16 0 1G 0 disk
`-sdb1 8:17 0 1024M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
sdc 8:32 0 1G 0 disk
`-sdc1 8:33 0 512M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
vda 252:0 0 25G 0 disk
`-vda1 252:1 0 25G 0 part /
where sdb is a SCSI disk with PCI controller 0000:00:0a.0 and ID=1,
sdc is an IDE disk with PCI controller 0000:00:01.1, and
vda is a virtio-blk disk with PCI device 0000:00:06.0,
guest-get-fsinfo command will return the following result:
{"return":
[{"name":"dm-1",
"mountpoint":"/mnt/test",
"disk":[
{"bus-type":"scsi","bus":0,"unit":1,"target":0,
"pci-controller":{"bus":0,"slot":10,"domain":0,"function":0}},
{"bus-type":"ide","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":1,"domain":0,"function":1}}],
"type":"xfs"},
{"name":"vda1", "mountpoint":"/",
"disk":[
{"bus-type":"virtio","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":6,"domain":0,"function":0}}],
"type":"ext4"}]}
In Linux guest, the disk information is resolved from sysfs. So far,
it only supports virtio-blk, virtio-scsi, IDE, SATA, SCSI disks on x86
hosts, and "disk" parameter may be empty for unsupported disk types.
Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama@hds.com>
*updated schema to report 2.2 as initial supported version
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2014-07-01 05:51:34 +08:00
|
|
|
return fs;
|
|
|
|
}
|
|
|
|
|
|
|
|
GuestFilesystemInfoList *qmp_guest_get_fsinfo(Error **errp)
|
|
|
|
{
|
|
|
|
FsMountList mounts;
|
|
|
|
struct FsMount *mount;
|
2020-11-13 09:13:37 +08:00
|
|
|
GuestFilesystemInfoList *ret = NULL;
|
qga: Add guest-get-fsinfo command
Add command to get mounted filesystems information in the guest.
The returned value contains a list of mountpoint paths and
corresponding disks info such as disk bus type, drive address,
and the disk controllers' PCI addresses, so that management layer
such as libvirt can resolve the disk backends.
For example, when `lsblk' result is:
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sdb 8:16 0 1G 0 disk
`-sdb1 8:17 0 1024M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
sdc 8:32 0 1G 0 disk
`-sdc1 8:33 0 512M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
vda 252:0 0 25G 0 disk
`-vda1 252:1 0 25G 0 part /
where sdb is a SCSI disk with PCI controller 0000:00:0a.0 and ID=1,
sdc is an IDE disk with PCI controller 0000:00:01.1, and
vda is a virtio-blk disk with PCI device 0000:00:06.0,
guest-get-fsinfo command will return the following result:
{"return":
[{"name":"dm-1",
"mountpoint":"/mnt/test",
"disk":[
{"bus-type":"scsi","bus":0,"unit":1,"target":0,
"pci-controller":{"bus":0,"slot":10,"domain":0,"function":0}},
{"bus-type":"ide","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":1,"domain":0,"function":1}}],
"type":"xfs"},
{"name":"vda1", "mountpoint":"/",
"disk":[
{"bus-type":"virtio","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":6,"domain":0,"function":0}}],
"type":"ext4"}]}
In Linux guest, the disk information is resolved from sysfs. So far,
it only supports virtio-blk, virtio-scsi, IDE, SATA, SCSI disks on x86
hosts, and "disk" parameter may be empty for unsupported disk types.
Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama@hds.com>
*updated schema to report 2.2 as initial supported version
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2014-07-01 05:51:34 +08:00
|
|
|
Error *local_err = NULL;
|
|
|
|
|
|
|
|
QTAILQ_INIT(&mounts);
|
2022-05-25 22:41:34 +08:00
|
|
|
if (!build_fs_mount_list(&mounts, &local_err)) {
|
qga: Add guest-get-fsinfo command
Add command to get mounted filesystems information in the guest.
The returned value contains a list of mountpoint paths and
corresponding disks info such as disk bus type, drive address,
and the disk controllers' PCI addresses, so that management layer
such as libvirt can resolve the disk backends.
For example, when `lsblk' result is:
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sdb 8:16 0 1G 0 disk
`-sdb1 8:17 0 1024M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
sdc 8:32 0 1G 0 disk
`-sdc1 8:33 0 512M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
vda 252:0 0 25G 0 disk
`-vda1 252:1 0 25G 0 part /
where sdb is a SCSI disk with PCI controller 0000:00:0a.0 and ID=1,
sdc is an IDE disk with PCI controller 0000:00:01.1, and
vda is a virtio-blk disk with PCI device 0000:00:06.0,
guest-get-fsinfo command will return the following result:
{"return":
[{"name":"dm-1",
"mountpoint":"/mnt/test",
"disk":[
{"bus-type":"scsi","bus":0,"unit":1,"target":0,
"pci-controller":{"bus":0,"slot":10,"domain":0,"function":0}},
{"bus-type":"ide","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":1,"domain":0,"function":1}}],
"type":"xfs"},
{"name":"vda1", "mountpoint":"/",
"disk":[
{"bus-type":"virtio","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":6,"domain":0,"function":0}}],
"type":"ext4"}]}
In Linux guest, the disk information is resolved from sysfs. So far,
it only supports virtio-blk, virtio-scsi, IDE, SATA, SCSI disks on x86
hosts, and "disk" parameter may be empty for unsupported disk types.
Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama@hds.com>
*updated schema to report 2.2 as initial supported version
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2014-07-01 05:51:34 +08:00
|
|
|
error_propagate(errp, local_err);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
QTAILQ_FOREACH(mount, &mounts, next) {
|
|
|
|
g_debug("Building guest fsinfo for '%s'", mount->dirname);
|
|
|
|
|
2020-11-13 09:13:37 +08:00
|
|
|
QAPI_LIST_PREPEND(ret, build_guest_fsinfo(mount, &local_err));
|
qga: Add guest-get-fsinfo command
Add command to get mounted filesystems information in the guest.
The returned value contains a list of mountpoint paths and
corresponding disks info such as disk bus type, drive address,
and the disk controllers' PCI addresses, so that management layer
such as libvirt can resolve the disk backends.
For example, when `lsblk' result is:
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sdb 8:16 0 1G 0 disk
`-sdb1 8:17 0 1024M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
sdc 8:32 0 1G 0 disk
`-sdc1 8:33 0 512M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
vda 252:0 0 25G 0 disk
`-vda1 252:1 0 25G 0 part /
where sdb is a SCSI disk with PCI controller 0000:00:0a.0 and ID=1,
sdc is an IDE disk with PCI controller 0000:00:01.1, and
vda is a virtio-blk disk with PCI device 0000:00:06.0,
guest-get-fsinfo command will return the following result:
{"return":
[{"name":"dm-1",
"mountpoint":"/mnt/test",
"disk":[
{"bus-type":"scsi","bus":0,"unit":1,"target":0,
"pci-controller":{"bus":0,"slot":10,"domain":0,"function":0}},
{"bus-type":"ide","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":1,"domain":0,"function":1}}],
"type":"xfs"},
{"name":"vda1", "mountpoint":"/",
"disk":[
{"bus-type":"virtio","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":6,"domain":0,"function":0}}],
"type":"ext4"}]}
In Linux guest, the disk information is resolved from sysfs. So far,
it only supports virtio-blk, virtio-scsi, IDE, SATA, SCSI disks on x86
hosts, and "disk" parameter may be empty for unsupported disk types.
Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama@hds.com>
*updated schema to report 2.2 as initial supported version
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2014-07-01 05:51:34 +08:00
|
|
|
if (local_err) {
|
|
|
|
error_propagate(errp, local_err);
|
|
|
|
qapi_free_GuestFilesystemInfoList(ret);
|
|
|
|
ret = NULL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
free_fs_mount_list(&mounts);
|
|
|
|
return ret;
|
|
|
|
}
|
2012-03-26 02:59:41 +08:00
|
|
|
#endif /* CONFIG_FSFREEZE */
|
2011-07-20 04:41:55 +08:00
|
|
|
|
2012-06-13 13:41:28 +08:00
|
|
|
#if defined(CONFIG_FSTRIM)
|
|
|
|
/*
|
|
|
|
* Walk list of mounted file systems in the guest, and trim them.
|
|
|
|
*/
|
2015-05-11 14:58:45 +08:00
|
|
|
GuestFilesystemTrimResponse *
|
|
|
|
qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp)
|
2012-06-13 13:41:28 +08:00
|
|
|
{
|
2015-05-11 14:58:45 +08:00
|
|
|
GuestFilesystemTrimResponse *response;
|
|
|
|
GuestFilesystemTrimResult *result;
|
2012-06-13 13:41:28 +08:00
|
|
|
int ret = 0;
|
|
|
|
FsMountList mounts;
|
|
|
|
struct FsMount *mount;
|
|
|
|
int fd;
|
2015-05-11 14:58:44 +08:00
|
|
|
struct fstrim_range r;
|
2012-06-13 13:41:28 +08:00
|
|
|
|
|
|
|
slog("guest-fstrim called");
|
|
|
|
|
|
|
|
QTAILQ_INIT(&mounts);
|
2022-05-25 22:41:34 +08:00
|
|
|
if (!build_fs_mount_list(&mounts, errp)) {
|
2015-05-11 14:58:45 +08:00
|
|
|
return NULL;
|
2012-06-13 13:41:28 +08:00
|
|
|
}
|
|
|
|
|
2015-05-11 14:58:45 +08:00
|
|
|
response = g_malloc0(sizeof(*response));
|
|
|
|
|
2012-06-13 13:41:28 +08:00
|
|
|
QTAILQ_FOREACH(mount, &mounts, next) {
|
2015-05-11 14:58:45 +08:00
|
|
|
result = g_malloc0(sizeof(*result));
|
|
|
|
result->path = g_strdup(mount->dirname);
|
|
|
|
|
2020-11-13 09:13:37 +08:00
|
|
|
QAPI_LIST_PREPEND(response->paths, result);
|
2015-05-11 14:58:45 +08:00
|
|
|
|
2022-05-25 22:41:33 +08:00
|
|
|
fd = qga_open_cloexec(mount->dirname, O_RDONLY, 0);
|
2012-06-13 13:41:28 +08:00
|
|
|
if (fd == -1) {
|
2015-05-11 14:58:45 +08:00
|
|
|
result->error = g_strdup_printf("failed to open: %s",
|
|
|
|
strerror(errno));
|
|
|
|
continue;
|
2012-06-13 13:41:28 +08:00
|
|
|
}
|
|
|
|
|
2016-05-18 21:11:55 +08:00
|
|
|
/* We try to cull filesystems we know won't work in advance, but other
|
|
|
|
* 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).
|
2015-05-11 14:58:45 +08:00
|
|
|
* Any other error means an unexpected error.
|
2012-06-13 13:41:28 +08:00
|
|
|
*/
|
2015-05-11 14:58:44 +08:00
|
|
|
r.start = 0;
|
|
|
|
r.len = -1;
|
|
|
|
r.minlen = has_minimum ? minimum : 0;
|
2012-06-13 13:41:28 +08:00
|
|
|
ret = ioctl(fd, FITRIM, &r);
|
|
|
|
if (ret == -1) {
|
2015-05-11 14:58:45 +08:00
|
|
|
if (errno == ENOTTY || errno == EOPNOTSUPP) {
|
|
|
|
result->error = g_strdup("trim not supported");
|
|
|
|
} else {
|
|
|
|
result->error = g_strdup_printf("failed to trim: %s",
|
|
|
|
strerror(errno));
|
2012-06-13 13:41:28 +08:00
|
|
|
}
|
2015-05-11 14:58:45 +08:00
|
|
|
close(fd);
|
|
|
|
continue;
|
2012-06-13 13:41:28 +08:00
|
|
|
}
|
2015-05-11 14:58:45 +08:00
|
|
|
|
|
|
|
result->has_minimum = true;
|
|
|
|
result->minimum = r.minlen;
|
|
|
|
result->has_trimmed = true;
|
|
|
|
result->trimmed = r.len;
|
2012-06-13 13:41:28 +08:00
|
|
|
close(fd);
|
|
|
|
}
|
|
|
|
|
|
|
|
free_fs_mount_list(&mounts);
|
2015-05-11 14:58:45 +08:00
|
|
|
return response;
|
2012-06-13 13:41:28 +08:00
|
|
|
}
|
|
|
|
#endif /* CONFIG_FSTRIM */
|
|
|
|
|
|
|
|
|
qemu-ga: add guest-suspend-disk
As the command name implies, this command suspends the guest to disk.
The suspend operation is implemented by two functions: bios_supports_mode()
and guest_suspend(). Both functions are generic enough to be used by
other suspend modes (introduced by next commits).
Both functions will try to use the scripts provided by the pm-utils
package if it's available. If it's not available, a manual method,
which consists of directly writing to '/sys/power/state', will be used.
To reap terminated children, a new signal handler is installed in the
parent to catch SIGCHLD signals and a non-blocking call to waitpid()
is done to collect their exit statuses. The statuses, however, are
discarded.
The approach used to query the guest for suspend support deserves some
explanation. It's implemented by bios_supports_mode() and shown below:
qemu-ga
|
create pipe
|
fork()
-----------------
| |
| |
| fork()
| --------------------------
| | |
| | |
| | exec('pm-is-supported')
| |
| wait()
| write exit status to pipe
| exit
|
read pipe
This might look complex, but the resulting code is quite simple.
The purpose of that approach is to allow qemu-ga to reap its children
(semi-)automatically from its SIGCHLD handler.
Implementing this the obvious way, that's, doing the exec() call from
the first child process, would force us to introduce a more complex way
to reap qemu-ga's children. Like registering PIDs to be reaped and
having a way to wait for them when returning their exit status to
qemu-ga is necessary. The approach explained above avoids that complexity.
Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
2012-02-28 22:03:03 +08:00
|
|
|
#define LINUX_SYS_STATE_FILE "/sys/power/state"
|
|
|
|
#define SUSPEND_SUPPORTED 0
|
|
|
|
#define SUSPEND_NOT_SUPPORTED 1
|
|
|
|
|
2018-06-21 18:21:51 +08:00
|
|
|
typedef enum {
|
|
|
|
SUSPEND_MODE_DISK = 0,
|
|
|
|
SUSPEND_MODE_RAM = 1,
|
|
|
|
SUSPEND_MODE_HYBRID = 2,
|
|
|
|
} SuspendMode;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Executes a command in a child process using g_spawn_sync,
|
|
|
|
* returning an int >= 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)
|
qemu-ga: add guest-suspend-disk
As the command name implies, this command suspends the guest to disk.
The suspend operation is implemented by two functions: bios_supports_mode()
and guest_suspend(). Both functions are generic enough to be used by
other suspend modes (introduced by next commits).
Both functions will try to use the scripts provided by the pm-utils
package if it's available. If it's not available, a manual method,
which consists of directly writing to '/sys/power/state', will be used.
To reap terminated children, a new signal handler is installed in the
parent to catch SIGCHLD signals and a non-blocking call to waitpid()
is done to collect their exit statuses. The statuses, however, are
discarded.
The approach used to query the guest for suspend support deserves some
explanation. It's implemented by bios_supports_mode() and shown below:
qemu-ga
|
create pipe
|
fork()
-----------------
| |
| |
| fork()
| --------------------------
| | |
| | |
| | exec('pm-is-supported')
| |
| wait()
| write exit status to pipe
| exit
|
read pipe
This might look complex, but the resulting code is quite simple.
The purpose of that approach is to allow qemu-ga to reap its children
(semi-)automatically from its SIGCHLD handler.
Implementing this the obvious way, that's, doing the exec() call from
the first child process, would force us to introduce a more complex way
to reap qemu-ga's children. Like registering PIDs to be reaped and
having a way to wait for them when returning their exit status to
qemu-ga is necessary. The approach explained above avoids that complexity.
Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
2012-02-28 22:03:03 +08:00
|
|
|
{
|
2018-06-21 18:21:51 +08:00
|
|
|
int exit_status, spawn_flag;
|
|
|
|
GError *g_err = NULL;
|
|
|
|
bool success;
|
|
|
|
|
|
|
|
spawn_flag = G_SPAWN_SEARCH_PATH | G_SPAWN_STDOUT_TO_DEV_NULL |
|
|
|
|
G_SPAWN_STDERR_TO_DEV_NULL;
|
qemu-ga: add guest-suspend-disk
As the command name implies, this command suspends the guest to disk.
The suspend operation is implemented by two functions: bios_supports_mode()
and guest_suspend(). Both functions are generic enough to be used by
other suspend modes (introduced by next commits).
Both functions will try to use the scripts provided by the pm-utils
package if it's available. If it's not available, a manual method,
which consists of directly writing to '/sys/power/state', will be used.
To reap terminated children, a new signal handler is installed in the
parent to catch SIGCHLD signals and a non-blocking call to waitpid()
is done to collect their exit statuses. The statuses, however, are
discarded.
The approach used to query the guest for suspend support deserves some
explanation. It's implemented by bios_supports_mode() and shown below:
qemu-ga
|
create pipe
|
fork()
-----------------
| |
| |
| fork()
| --------------------------
| | |
| | |
| | exec('pm-is-supported')
| |
| wait()
| write exit status to pipe
| exit
|
read pipe
This might look complex, but the resulting code is quite simple.
The purpose of that approach is to allow qemu-ga to reap its children
(semi-)automatically from its SIGCHLD handler.
Implementing this the obvious way, that's, doing the exec() call from
the first child process, would force us to introduce a more complex way
to reap qemu-ga's children. Like registering PIDs to be reaped and
having a way to wait for them when returning their exit status to
qemu-ga is necessary. The approach explained above avoids that complexity.
Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
2012-02-28 22:03:03 +08:00
|
|
|
|
2022-03-23 23:57:41 +08:00
|
|
|
success = g_spawn_sync(NULL, (char **)command, NULL, spawn_flag,
|
2018-06-21 18:21:51 +08:00
|
|
|
NULL, NULL, NULL, NULL,
|
|
|
|
&exit_status, &g_err);
|
2018-06-21 18:21:48 +08:00
|
|
|
|
2018-06-21 18:21:51 +08:00
|
|
|
if (success) {
|
|
|
|
return WEXITSTATUS(exit_status);
|
2018-06-21 18:21:48 +08:00
|
|
|
}
|
|
|
|
|
2018-06-21 18:21:51 +08:00
|
|
|
if (g_err && (g_err->code != G_SPAWN_ERROR_NOENT)) {
|
|
|
|
error_setg(errp, "failed to create child process, error '%s'",
|
|
|
|
g_err->message);
|
2018-06-21 18:21:49 +08:00
|
|
|
}
|
qemu-ga: add guest-suspend-disk
As the command name implies, this command suspends the guest to disk.
The suspend operation is implemented by two functions: bios_supports_mode()
and guest_suspend(). Both functions are generic enough to be used by
other suspend modes (introduced by next commits).
Both functions will try to use the scripts provided by the pm-utils
package if it's available. If it's not available, a manual method,
which consists of directly writing to '/sys/power/state', will be used.
To reap terminated children, a new signal handler is installed in the
parent to catch SIGCHLD signals and a non-blocking call to waitpid()
is done to collect their exit statuses. The statuses, however, are
discarded.
The approach used to query the guest for suspend support deserves some
explanation. It's implemented by bios_supports_mode() and shown below:
qemu-ga
|
create pipe
|
fork()
-----------------
| |
| |
| fork()
| --------------------------
| | |
| | |
| | exec('pm-is-supported')
| |
| wait()
| write exit status to pipe
| exit
|
read pipe
This might look complex, but the resulting code is quite simple.
The purpose of that approach is to allow qemu-ga to reap its children
(semi-)automatically from its SIGCHLD handler.
Implementing this the obvious way, that's, doing the exec() call from
the first child process, would force us to introduce a more complex way
to reap qemu-ga's children. Like registering PIDs to be reaped and
having a way to wait for them when returning their exit status to
qemu-ga is necessary. The approach explained above avoids that complexity.
Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
2012-02-28 22:03:03 +08:00
|
|
|
|
2018-06-21 18:21:51 +08:00
|
|
|
g_error_free(g_err);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2018-06-21 18:21:52 +08:00
|
|
|
static bool systemd_supports_mode(SuspendMode mode, Error **errp)
|
|
|
|
{
|
|
|
|
const char *systemctl_args[3] = {"systemd-hibernate", "systemd-suspend",
|
|
|
|
"systemd-hybrid-sleep"};
|
|
|
|
const char *cmd[4] = {"systemctl", "status", systemctl_args[mode], NULL};
|
|
|
|
int status;
|
|
|
|
|
2020-07-08 00:06:04 +08:00
|
|
|
status = run_process_child(cmd, errp);
|
2018-06-21 18:21:52 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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 = 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 = NULL;
|
|
|
|
const char *systemctl_args[3] = {"hibernate", "suspend", "hybrid-sleep"};
|
|
|
|
const char *cmd[3] = {"systemctl", systemctl_args[mode], NULL};
|
|
|
|
int status;
|
|
|
|
|
|
|
|
status = run_process_child(cmd, &local_err);
|
|
|
|
|
|
|
|
if (status == 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((status == -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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-21 18:21:51 +08:00
|
|
|
static bool pmutils_supports_mode(SuspendMode mode, Error **errp)
|
|
|
|
{
|
|
|
|
Error *local_err = NULL;
|
|
|
|
const char *pmutils_args[3] = {"--hibernate", "--suspend",
|
|
|
|
"--suspend-hybrid"};
|
|
|
|
const char *cmd[3] = {"pm-is-supported", pmutils_args[mode], NULL};
|
|
|
|
int status;
|
|
|
|
|
|
|
|
status = run_process_child(cmd, &local_err);
|
|
|
|
|
|
|
|
if (status == SUSPEND_SUPPORTED) {
|
|
|
|
return true;
|
qemu-ga: add guest-suspend-disk
As the command name implies, this command suspends the guest to disk.
The suspend operation is implemented by two functions: bios_supports_mode()
and guest_suspend(). Both functions are generic enough to be used by
other suspend modes (introduced by next commits).
Both functions will try to use the scripts provided by the pm-utils
package if it's available. If it's not available, a manual method,
which consists of directly writing to '/sys/power/state', will be used.
To reap terminated children, a new signal handler is installed in the
parent to catch SIGCHLD signals and a non-blocking call to waitpid()
is done to collect their exit statuses. The statuses, however, are
discarded.
The approach used to query the guest for suspend support deserves some
explanation. It's implemented by bios_supports_mode() and shown below:
qemu-ga
|
create pipe
|
fork()
-----------------
| |
| |
| fork()
| --------------------------
| | |
| | |
| | exec('pm-is-supported')
| |
| wait()
| write exit status to pipe
| exit
|
read pipe
This might look complex, but the resulting code is quite simple.
The purpose of that approach is to allow qemu-ga to reap its children
(semi-)automatically from its SIGCHLD handler.
Implementing this the obvious way, that's, doing the exec() call from
the first child process, would force us to introduce a more complex way
to reap qemu-ga's children. Like registering PIDs to be reaped and
having a way to wait for them when returning their exit status to
qemu-ga is necessary. The approach explained above avoids that complexity.
Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
2012-02-28 22:03:03 +08:00
|
|
|
}
|
|
|
|
|
2018-06-21 18:21:51 +08:00
|
|
|
if ((status == -1) && !local_err) {
|
|
|
|
return false;
|
2012-11-27 21:02:03 +08:00
|
|
|
}
|
qemu-ga: add guest-suspend-disk
As the command name implies, this command suspends the guest to disk.
The suspend operation is implemented by two functions: bios_supports_mode()
and guest_suspend(). Both functions are generic enough to be used by
other suspend modes (introduced by next commits).
Both functions will try to use the scripts provided by the pm-utils
package if it's available. If it's not available, a manual method,
which consists of directly writing to '/sys/power/state', will be used.
To reap terminated children, a new signal handler is installed in the
parent to catch SIGCHLD signals and a non-blocking call to waitpid()
is done to collect their exit statuses. The statuses, however, are
discarded.
The approach used to query the guest for suspend support deserves some
explanation. It's implemented by bios_supports_mode() and shown below:
qemu-ga
|
create pipe
|
fork()
-----------------
| |
| |
| fork()
| --------------------------
| | |
| | |
| | exec('pm-is-supported')
| |
| wait()
| write exit status to pipe
| exit
|
read pipe
This might look complex, but the resulting code is quite simple.
The purpose of that approach is to allow qemu-ga to reap its children
(semi-)automatically from its SIGCHLD handler.
Implementing this the obvious way, that's, doing the exec() call from
the first child process, would force us to introduce a more complex way
to reap qemu-ga's children. Like registering PIDs to be reaped and
having a way to wait for them when returning their exit status to
qemu-ga is necessary. The approach explained above avoids that complexity.
Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
2012-02-28 22:03:03 +08:00
|
|
|
|
2018-06-21 18:21:51 +08:00
|
|
|
if (local_err) {
|
|
|
|
error_propagate(errp, local_err);
|
|
|
|
} else {
|
2014-05-02 19:26:30 +08:00
|
|
|
error_setg(errp,
|
2018-06-21 18:21:51 +08:00
|
|
|
"the helper program '%s' returned an unexpected exit"
|
|
|
|
" status code (%d)", "pm-is-supported", status);
|
qemu-ga: add guest-suspend-disk
As the command name implies, this command suspends the guest to disk.
The suspend operation is implemented by two functions: bios_supports_mode()
and guest_suspend(). Both functions are generic enough to be used by
other suspend modes (introduced by next commits).
Both functions will try to use the scripts provided by the pm-utils
package if it's available. If it's not available, a manual method,
which consists of directly writing to '/sys/power/state', will be used.
To reap terminated children, a new signal handler is installed in the
parent to catch SIGCHLD signals and a non-blocking call to waitpid()
is done to collect their exit statuses. The statuses, however, are
discarded.
The approach used to query the guest for suspend support deserves some
explanation. It's implemented by bios_supports_mode() and shown below:
qemu-ga
|
create pipe
|
fork()
-----------------
| |
| |
| fork()
| --------------------------
| | |
| | |
| | exec('pm-is-supported')
| |
| wait()
| write exit status to pipe
| exit
|
read pipe
This might look complex, but the resulting code is quite simple.
The purpose of that approach is to allow qemu-ga to reap its children
(semi-)automatically from its SIGCHLD handler.
Implementing this the obvious way, that's, doing the exec() call from
the first child process, would force us to introduce a more complex way
to reap qemu-ga's children. Like registering PIDs to be reaped and
having a way to wait for them when returning their exit status to
qemu-ga is necessary. The approach explained above avoids that complexity.
Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
2012-02-28 22:03:03 +08:00
|
|
|
}
|
|
|
|
|
2018-06-21 18:21:51 +08:00
|
|
|
return false;
|
2018-06-21 18:21:49 +08:00
|
|
|
}
|
|
|
|
|
2018-06-21 18:21:51 +08:00
|
|
|
static void pmutils_suspend(SuspendMode mode, Error **errp)
|
2018-06-21 18:21:50 +08:00
|
|
|
{
|
|
|
|
Error *local_err = NULL;
|
2018-06-21 18:21:51 +08:00
|
|
|
const char *pmutils_binaries[3] = {"pm-hibernate", "pm-suspend",
|
|
|
|
"pm-suspend-hybrid"};
|
|
|
|
const char *cmd[2] = {pmutils_binaries[mode], NULL};
|
2018-06-21 18:21:50 +08:00
|
|
|
int status;
|
|
|
|
|
2018-06-21 18:21:51 +08:00
|
|
|
status = run_process_child(cmd, &local_err);
|
2018-06-21 18:21:50 +08:00
|
|
|
|
2018-06-21 18:21:51 +08:00
|
|
|
if (status == 0) {
|
2018-06-21 18:21:50 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-06-21 18:21:51 +08:00
|
|
|
if ((status == -1) && !local_err) {
|
|
|
|
error_setg(errp, "the helper program '%s' was not found",
|
|
|
|
pmutils_binaries[mode]);
|
|
|
|
return;
|
2018-06-21 18:21:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (local_err) {
|
|
|
|
error_propagate(errp, local_err);
|
2018-06-21 18:21:51 +08:00
|
|
|
} else {
|
2018-06-21 18:21:50 +08:00
|
|
|
error_setg(errp,
|
2018-06-21 18:21:51 +08:00
|
|
|
"the helper program '%s' returned an unexpected exit"
|
|
|
|
" status code (%d)", pmutils_binaries[mode], status);
|
2018-06-21 18:21:50 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-21 18:21:51 +08:00
|
|
|
static bool linux_sys_state_supports_mode(SuspendMode mode, Error **errp)
|
2018-06-21 18:21:49 +08:00
|
|
|
{
|
2018-06-21 18:21:51 +08:00
|
|
|
const char *sysfile_strs[3] = {"disk", "mem", NULL};
|
|
|
|
const char *sysfile_str = sysfile_strs[mode];
|
2018-06-21 18:21:49 +08:00
|
|
|
char buf[32]; /* hopefully big enough */
|
|
|
|
int fd;
|
|
|
|
ssize_t ret;
|
|
|
|
|
2018-06-21 18:21:51 +08:00
|
|
|
if (!sysfile_str) {
|
|
|
|
error_setg(errp, "unknown guest suspend mode");
|
2018-06-21 18:21:49 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
fd = open(LINUX_SYS_STATE_FILE, O_RDONLY);
|
|
|
|
if (fd < 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = read(fd, buf, sizeof(buf) - 1);
|
2018-07-16 00:21:56 +08:00
|
|
|
close(fd);
|
2018-06-21 18:21:49 +08:00
|
|
|
if (ret <= 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
buf[ret] = '\0';
|
|
|
|
|
|
|
|
if (strstr(buf, sysfile_str)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-06-21 18:21:51 +08:00
|
|
|
static void linux_sys_state_suspend(SuspendMode mode, Error **errp)
|
qemu-ga: add guest-suspend-disk
As the command name implies, this command suspends the guest to disk.
The suspend operation is implemented by two functions: bios_supports_mode()
and guest_suspend(). Both functions are generic enough to be used by
other suspend modes (introduced by next commits).
Both functions will try to use the scripts provided by the pm-utils
package if it's available. If it's not available, a manual method,
which consists of directly writing to '/sys/power/state', will be used.
To reap terminated children, a new signal handler is installed in the
parent to catch SIGCHLD signals and a non-blocking call to waitpid()
is done to collect their exit statuses. The statuses, however, are
discarded.
The approach used to query the guest for suspend support deserves some
explanation. It's implemented by bios_supports_mode() and shown below:
qemu-ga
|
create pipe
|
fork()
-----------------
| |
| |
| fork()
| --------------------------
| | |
| | |
| | exec('pm-is-supported')
| |
| wait()
| write exit status to pipe
| exit
|
read pipe
This might look complex, but the resulting code is quite simple.
The purpose of that approach is to allow qemu-ga to reap its children
(semi-)automatically from its SIGCHLD handler.
Implementing this the obvious way, that's, doing the exec() call from
the first child process, would force us to introduce a more complex way
to reap qemu-ga's children. Like registering PIDs to be reaped and
having a way to wait for them when returning their exit status to
qemu-ga is necessary. The approach explained above avoids that complexity.
Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
2012-02-28 22:03:03 +08:00
|
|
|
{
|
2012-11-27 21:02:04 +08:00
|
|
|
Error *local_err = NULL;
|
2018-06-21 18:21:51 +08:00
|
|
|
const char *sysfile_strs[3] = {"disk", "mem", NULL};
|
|
|
|
const char *sysfile_str = sysfile_strs[mode];
|
2012-11-27 21:02:04 +08:00
|
|
|
pid_t pid;
|
qemu-ga: guest-suspend: make the API synchronous
Currently, qemu-ga has a SIGCHLD handler that automatically reaps terminated
children processes. The idea is to avoid having qemu-ga commands blocked
waiting for children to terminate.
That approach has two problems:
1. qemu-ga is unable to detect errors in the child, meaning that qemu-ga
returns success even if the child fails to perform its task
2. if a command does depend on the child exit status, the command has to
play tricks to bypass the automatic reaper
Case 2 impacts the guest-suspend-* API, because it has to execute an external
program to check for suspend support. Today, to bypass the automatic reaper,
suspend code has to double fork and pass exit status information through a
pipe. Besides being complex, this is prone to race condition bugs. Indeed,
the current code does have such bugs.
Making the guest-suspend-* API synchronous (ie. by dropping the SIGCHLD
handler and calling waitpid() from commands) is a much simpler approach,
which fixes current race conditions bugs and enables commands to detect
errors in the child.
This commit does just that. There's a side effect though, guest-shutdown
will generate zombies if shutting down fails. This will be fixed by the
next commit.
Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2012-05-12 03:19:46 +08:00
|
|
|
int status;
|
qemu-ga: add guest-suspend-disk
As the command name implies, this command suspends the guest to disk.
The suspend operation is implemented by two functions: bios_supports_mode()
and guest_suspend(). Both functions are generic enough to be used by
other suspend modes (introduced by next commits).
Both functions will try to use the scripts provided by the pm-utils
package if it's available. If it's not available, a manual method,
which consists of directly writing to '/sys/power/state', will be used.
To reap terminated children, a new signal handler is installed in the
parent to catch SIGCHLD signals and a non-blocking call to waitpid()
is done to collect their exit statuses. The statuses, however, are
discarded.
The approach used to query the guest for suspend support deserves some
explanation. It's implemented by bios_supports_mode() and shown below:
qemu-ga
|
create pipe
|
fork()
-----------------
| |
| |
| fork()
| --------------------------
| | |
| | |
| | exec('pm-is-supported')
| |
| wait()
| write exit status to pipe
| exit
|
read pipe
This might look complex, but the resulting code is quite simple.
The purpose of that approach is to allow qemu-ga to reap its children
(semi-)automatically from its SIGCHLD handler.
Implementing this the obvious way, that's, doing the exec() call from
the first child process, would force us to introduce a more complex way
to reap qemu-ga's children. Like registering PIDs to be reaped and
having a way to wait for them when returning their exit status to
qemu-ga is necessary. The approach explained above avoids that complexity.
Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
2012-02-28 22:03:03 +08:00
|
|
|
|
2018-06-21 18:21:51 +08:00
|
|
|
if (!sysfile_str) {
|
2018-06-21 18:21:48 +08:00
|
|
|
error_setg(errp, "unknown guest suspend mode");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
qemu-ga: add guest-suspend-disk
As the command name implies, this command suspends the guest to disk.
The suspend operation is implemented by two functions: bios_supports_mode()
and guest_suspend(). Both functions are generic enough to be used by
other suspend modes (introduced by next commits).
Both functions will try to use the scripts provided by the pm-utils
package if it's available. If it's not available, a manual method,
which consists of directly writing to '/sys/power/state', will be used.
To reap terminated children, a new signal handler is installed in the
parent to catch SIGCHLD signals and a non-blocking call to waitpid()
is done to collect their exit statuses. The statuses, however, are
discarded.
The approach used to query the guest for suspend support deserves some
explanation. It's implemented by bios_supports_mode() and shown below:
qemu-ga
|
create pipe
|
fork()
-----------------
| |
| |
| fork()
| --------------------------
| | |
| | |
| | exec('pm-is-supported')
| |
| wait()
| write exit status to pipe
| exit
|
read pipe
This might look complex, but the resulting code is quite simple.
The purpose of that approach is to allow qemu-ga to reap its children
(semi-)automatically from its SIGCHLD handler.
Implementing this the obvious way, that's, doing the exec() call from
the first child process, would force us to introduce a more complex way
to reap qemu-ga's children. Like registering PIDs to be reaped and
having a way to wait for them when returning their exit status to
qemu-ga is necessary. The approach explained above avoids that complexity.
Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
2012-02-28 22:03:03 +08:00
|
|
|
pid = fork();
|
2018-06-21 18:21:50 +08:00
|
|
|
if (!pid) {
|
qemu-ga: add guest-suspend-disk
As the command name implies, this command suspends the guest to disk.
The suspend operation is implemented by two functions: bios_supports_mode()
and guest_suspend(). Both functions are generic enough to be used by
other suspend modes (introduced by next commits).
Both functions will try to use the scripts provided by the pm-utils
package if it's available. If it's not available, a manual method,
which consists of directly writing to '/sys/power/state', will be used.
To reap terminated children, a new signal handler is installed in the
parent to catch SIGCHLD signals and a non-blocking call to waitpid()
is done to collect their exit statuses. The statuses, however, are
discarded.
The approach used to query the guest for suspend support deserves some
explanation. It's implemented by bios_supports_mode() and shown below:
qemu-ga
|
create pipe
|
fork()
-----------------
| |
| |
| fork()
| --------------------------
| | |
| | |
| | exec('pm-is-supported')
| |
| wait()
| write exit status to pipe
| exit
|
read pipe
This might look complex, but the resulting code is quite simple.
The purpose of that approach is to allow qemu-ga to reap its children
(semi-)automatically from its SIGCHLD handler.
Implementing this the obvious way, that's, doing the exec() call from
the first child process, would force us to introduce a more complex way
to reap qemu-ga's children. Like registering PIDs to be reaped and
having a way to wait for them when returning their exit status to
qemu-ga is necessary. The approach explained above avoids that complexity.
Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
2012-02-28 22:03:03 +08:00
|
|
|
/* child */
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
setsid();
|
|
|
|
reopen_fd_to_null(0);
|
|
|
|
reopen_fd_to_null(1);
|
|
|
|
reopen_fd_to_null(2);
|
|
|
|
|
|
|
|
fd = 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);
|
2012-11-27 21:02:04 +08:00
|
|
|
} else if (pid < 0) {
|
2014-05-02 19:26:30 +08:00
|
|
|
error_setg_errno(errp, errno, "failed to create child process");
|
2018-06-21 18:21:50 +08:00
|
|
|
return;
|
qemu-ga: add guest-suspend-disk
As the command name implies, this command suspends the guest to disk.
The suspend operation is implemented by two functions: bios_supports_mode()
and guest_suspend(). Both functions are generic enough to be used by
other suspend modes (introduced by next commits).
Both functions will try to use the scripts provided by the pm-utils
package if it's available. If it's not available, a manual method,
which consists of directly writing to '/sys/power/state', will be used.
To reap terminated children, a new signal handler is installed in the
parent to catch SIGCHLD signals and a non-blocking call to waitpid()
is done to collect their exit statuses. The statuses, however, are
discarded.
The approach used to query the guest for suspend support deserves some
explanation. It's implemented by bios_supports_mode() and shown below:
qemu-ga
|
create pipe
|
fork()
-----------------
| |
| |
| fork()
| --------------------------
| | |
| | |
| | exec('pm-is-supported')
| |
| wait()
| write exit status to pipe
| exit
|
read pipe
This might look complex, but the resulting code is quite simple.
The purpose of that approach is to allow qemu-ga to reap its children
(semi-)automatically from its SIGCHLD handler.
Implementing this the obvious way, that's, doing the exec() call from
the first child process, would force us to introduce a more complex way
to reap qemu-ga's children. Like registering PIDs to be reaped and
having a way to wait for them when returning their exit status to
qemu-ga is necessary. The approach explained above avoids that complexity.
Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
2012-02-28 22:03:03 +08:00
|
|
|
}
|
|
|
|
|
2012-11-27 21:02:04 +08:00
|
|
|
ga_wait_child(pid, &status, &local_err);
|
2014-01-30 22:07:28 +08:00
|
|
|
if (local_err) {
|
2014-05-02 19:26:30 +08:00
|
|
|
error_propagate(errp, local_err);
|
2018-06-21 18:21:50 +08:00
|
|
|
return;
|
qemu-ga: guest-suspend: make the API synchronous
Currently, qemu-ga has a SIGCHLD handler that automatically reaps terminated
children processes. The idea is to avoid having qemu-ga commands blocked
waiting for children to terminate.
That approach has two problems:
1. qemu-ga is unable to detect errors in the child, meaning that qemu-ga
returns success even if the child fails to perform its task
2. if a command does depend on the child exit status, the command has to
play tricks to bypass the automatic reaper
Case 2 impacts the guest-suspend-* API, because it has to execute an external
program to check for suspend support. Today, to bypass the automatic reaper,
suspend code has to double fork and pass exit status information through a
pipe. Besides being complex, this is prone to race condition bugs. Indeed,
the current code does have such bugs.
Making the guest-suspend-* API synchronous (ie. by dropping the SIGCHLD
handler and calling waitpid() from commands) is a much simpler approach,
which fixes current race conditions bugs and enables commands to detect
errors in the child.
This commit does just that. There's a side effect though, guest-shutdown
will generate zombies if shutting down fails. This will be fixed by the
next commit.
Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2012-05-12 03:19:46 +08:00
|
|
|
}
|
|
|
|
|
2012-11-27 21:02:04 +08:00
|
|
|
if (WEXITSTATUS(status)) {
|
2014-05-02 19:26:30 +08:00
|
|
|
error_setg(errp, "child process has failed to suspend");
|
qemu-ga: add guest-suspend-disk
As the command name implies, this command suspends the guest to disk.
The suspend operation is implemented by two functions: bios_supports_mode()
and guest_suspend(). Both functions are generic enough to be used by
other suspend modes (introduced by next commits).
Both functions will try to use the scripts provided by the pm-utils
package if it's available. If it's not available, a manual method,
which consists of directly writing to '/sys/power/state', will be used.
To reap terminated children, a new signal handler is installed in the
parent to catch SIGCHLD signals and a non-blocking call to waitpid()
is done to collect their exit statuses. The statuses, however, are
discarded.
The approach used to query the guest for suspend support deserves some
explanation. It's implemented by bios_supports_mode() and shown below:
qemu-ga
|
create pipe
|
fork()
-----------------
| |
| |
| fork()
| --------------------------
| | |
| | |
| | exec('pm-is-supported')
| |
| wait()
| write exit status to pipe
| exit
|
read pipe
This might look complex, but the resulting code is quite simple.
The purpose of that approach is to allow qemu-ga to reap its children
(semi-)automatically from its SIGCHLD handler.
Implementing this the obvious way, that's, doing the exec() call from
the first child process, would force us to introduce a more complex way
to reap qemu-ga's children. Like registering PIDs to be reaped and
having a way to wait for them when returning their exit status to
qemu-ga is necessary. The approach explained above avoids that complexity.
Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
2012-02-28 22:03:03 +08:00
|
|
|
}
|
qemu-ga: guest-suspend: make the API synchronous
Currently, qemu-ga has a SIGCHLD handler that automatically reaps terminated
children processes. The idea is to avoid having qemu-ga commands blocked
waiting for children to terminate.
That approach has two problems:
1. qemu-ga is unable to detect errors in the child, meaning that qemu-ga
returns success even if the child fails to perform its task
2. if a command does depend on the child exit status, the command has to
play tricks to bypass the automatic reaper
Case 2 impacts the guest-suspend-* API, because it has to execute an external
program to check for suspend support. Today, to bypass the automatic reaper,
suspend code has to double fork and pass exit status information through a
pipe. Besides being complex, this is prone to race condition bugs. Indeed,
the current code does have such bugs.
Making the guest-suspend-* API synchronous (ie. by dropping the SIGCHLD
handler and calling waitpid() from commands) is a much simpler approach,
which fixes current race conditions bugs and enables commands to detect
errors in the child.
This commit does just that. There's a side effect though, guest-shutdown
will generate zombies if shutting down fails. This will be fixed by the
next commit.
Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2012-05-12 03:19:46 +08:00
|
|
|
|
2018-06-21 18:21:50 +08:00
|
|
|
}
|
|
|
|
|
2018-06-21 18:21:51 +08:00
|
|
|
static void guest_suspend(SuspendMode mode, Error **errp)
|
2018-06-21 18:21:50 +08:00
|
|
|
{
|
|
|
|
Error *local_err = NULL;
|
qga: removing bios_supports_mode
bios_support_mode verifies if the guest has support for a certain
suspend mode but it doesn't inform back which suspend tool
provides it. The caller, guest_suspend, executes all suspend
strategies in order again.
After adding systemd suspend support, bios_support_mode now will
verify for support for systemd, then pmutils, then Linux sys state
file. In a worst case scenario where both systemd and pmutils isn't
supported but Linux sys state is:
- bios_supports_mode will check for systemd, then pmutils, then
Linux sys state. It will tell guest_suspend that there is support,
but it will not tell who provides it;
- guest_suspend will try to execute (and fail) systemd suspend,
then pmutils suspend, to only then use the Linux sys suspend.
The time spent executing systemd and pmutils suspend was wasted
and could be avoided, but only bios_support_mode knew it but
didn't inform it back.
A quicker approach is to nuke bios_supports_mode and control
whether we found support at all with a bool flag inside
guest_suspend. guest_suspend will search for suspend support
and execute it as soon as possible. If the a given suspend
mechanism fails, continue to the next. If no suspend
support is found, the "not supported" message is still being
sent back to the user.
Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com>
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2018-06-21 18:21:53 +08:00
|
|
|
bool mode_supported = false;
|
2018-06-21 18:21:50 +08:00
|
|
|
|
qga: removing bios_supports_mode
bios_support_mode verifies if the guest has support for a certain
suspend mode but it doesn't inform back which suspend tool
provides it. The caller, guest_suspend, executes all suspend
strategies in order again.
After adding systemd suspend support, bios_support_mode now will
verify for support for systemd, then pmutils, then Linux sys state
file. In a worst case scenario where both systemd and pmutils isn't
supported but Linux sys state is:
- bios_supports_mode will check for systemd, then pmutils, then
Linux sys state. It will tell guest_suspend that there is support,
but it will not tell who provides it;
- guest_suspend will try to execute (and fail) systemd suspend,
then pmutils suspend, to only then use the Linux sys suspend.
The time spent executing systemd and pmutils suspend was wasted
and could be avoided, but only bios_support_mode knew it but
didn't inform it back.
A quicker approach is to nuke bios_supports_mode and control
whether we found support at all with a bool flag inside
guest_suspend. guest_suspend will search for suspend support
and execute it as soon as possible. If the a given suspend
mechanism fails, continue to the next. If no suspend
support is found, the "not supported" message is still being
sent back to the user.
Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com>
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2018-06-21 18:21:53 +08:00
|
|
|
if (systemd_supports_mode(mode, &local_err)) {
|
|
|
|
mode_supported = true;
|
|
|
|
systemd_suspend(mode, &local_err);
|
2018-06-21 18:21:50 +08:00
|
|
|
|
2023-04-06 20:45:31 +08:00
|
|
|
if (!local_err) {
|
|
|
|
return;
|
|
|
|
}
|
2018-06-21 18:21:52 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
error_free(local_err);
|
2020-03-24 23:36:30 +08:00
|
|
|
local_err = NULL;
|
2018-06-21 18:21:52 +08:00
|
|
|
|
qga: removing bios_supports_mode
bios_support_mode verifies if the guest has support for a certain
suspend mode but it doesn't inform back which suspend tool
provides it. The caller, guest_suspend, executes all suspend
strategies in order again.
After adding systemd suspend support, bios_support_mode now will
verify for support for systemd, then pmutils, then Linux sys state
file. In a worst case scenario where both systemd and pmutils isn't
supported but Linux sys state is:
- bios_supports_mode will check for systemd, then pmutils, then
Linux sys state. It will tell guest_suspend that there is support,
but it will not tell who provides it;
- guest_suspend will try to execute (and fail) systemd suspend,
then pmutils suspend, to only then use the Linux sys suspend.
The time spent executing systemd and pmutils suspend was wasted
and could be avoided, but only bios_support_mode knew it but
didn't inform it back.
A quicker approach is to nuke bios_supports_mode and control
whether we found support at all with a bool flag inside
guest_suspend. guest_suspend will search for suspend support
and execute it as soon as possible. If the a given suspend
mechanism fails, continue to the next. If no suspend
support is found, the "not supported" message is still being
sent back to the user.
Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com>
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2018-06-21 18:21:53 +08:00
|
|
|
if (pmutils_supports_mode(mode, &local_err)) {
|
|
|
|
mode_supported = true;
|
|
|
|
pmutils_suspend(mode, &local_err);
|
|
|
|
|
2023-04-06 20:45:31 +08:00
|
|
|
if (!local_err) {
|
|
|
|
return;
|
|
|
|
}
|
2018-06-21 18:21:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
error_free(local_err);
|
2020-03-24 23:36:30 +08:00
|
|
|
local_err = NULL;
|
2018-06-21 18:21:50 +08:00
|
|
|
|
qga: removing bios_supports_mode
bios_support_mode verifies if the guest has support for a certain
suspend mode but it doesn't inform back which suspend tool
provides it. The caller, guest_suspend, executes all suspend
strategies in order again.
After adding systemd suspend support, bios_support_mode now will
verify for support for systemd, then pmutils, then Linux sys state
file. In a worst case scenario where both systemd and pmutils isn't
supported but Linux sys state is:
- bios_supports_mode will check for systemd, then pmutils, then
Linux sys state. It will tell guest_suspend that there is support,
but it will not tell who provides it;
- guest_suspend will try to execute (and fail) systemd suspend,
then pmutils suspend, to only then use the Linux sys suspend.
The time spent executing systemd and pmutils suspend was wasted
and could be avoided, but only bios_support_mode knew it but
didn't inform it back.
A quicker approach is to nuke bios_supports_mode and control
whether we found support at all with a bool flag inside
guest_suspend. guest_suspend will search for suspend support
and execute it as soon as possible. If the a given suspend
mechanism fails, continue to the next. If no suspend
support is found, the "not supported" message is still being
sent back to the user.
Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com>
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2018-06-21 18:21:53 +08:00
|
|
|
if (linux_sys_state_supports_mode(mode, &local_err)) {
|
|
|
|
mode_supported = true;
|
|
|
|
linux_sys_state_suspend(mode, &local_err);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!mode_supported) {
|
2020-03-24 23:36:30 +08:00
|
|
|
error_free(local_err);
|
qga: removing bios_supports_mode
bios_support_mode verifies if the guest has support for a certain
suspend mode but it doesn't inform back which suspend tool
provides it. The caller, guest_suspend, executes all suspend
strategies in order again.
After adding systemd suspend support, bios_support_mode now will
verify for support for systemd, then pmutils, then Linux sys state
file. In a worst case scenario where both systemd and pmutils isn't
supported but Linux sys state is:
- bios_supports_mode will check for systemd, then pmutils, then
Linux sys state. It will tell guest_suspend that there is support,
but it will not tell who provides it;
- guest_suspend will try to execute (and fail) systemd suspend,
then pmutils suspend, to only then use the Linux sys suspend.
The time spent executing systemd and pmutils suspend was wasted
and could be avoided, but only bios_support_mode knew it but
didn't inform it back.
A quicker approach is to nuke bios_supports_mode and control
whether we found support at all with a bool flag inside
guest_suspend. guest_suspend will search for suspend support
and execute it as soon as possible. If the a given suspend
mechanism fails, continue to the next. If no suspend
support is found, the "not supported" message is still being
sent back to the user.
Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com>
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2018-06-21 18:21:53 +08:00
|
|
|
error_setg(errp,
|
|
|
|
"the requested suspend mode is not supported by the guest");
|
2018-12-14 01:31:13 +08:00
|
|
|
} else {
|
2018-06-21 18:21:50 +08:00
|
|
|
error_propagate(errp, local_err);
|
|
|
|
}
|
qemu-ga: add guest-suspend-disk
As the command name implies, this command suspends the guest to disk.
The suspend operation is implemented by two functions: bios_supports_mode()
and guest_suspend(). Both functions are generic enough to be used by
other suspend modes (introduced by next commits).
Both functions will try to use the scripts provided by the pm-utils
package if it's available. If it's not available, a manual method,
which consists of directly writing to '/sys/power/state', will be used.
To reap terminated children, a new signal handler is installed in the
parent to catch SIGCHLD signals and a non-blocking call to waitpid()
is done to collect their exit statuses. The statuses, however, are
discarded.
The approach used to query the guest for suspend support deserves some
explanation. It's implemented by bios_supports_mode() and shown below:
qemu-ga
|
create pipe
|
fork()
-----------------
| |
| |
| fork()
| --------------------------
| | |
| | |
| | exec('pm-is-supported')
| |
| wait()
| write exit status to pipe
| exit
|
read pipe
This might look complex, but the resulting code is quite simple.
The purpose of that approach is to allow qemu-ga to reap its children
(semi-)automatically from its SIGCHLD handler.
Implementing this the obvious way, that's, doing the exec() call from
the first child process, would force us to introduce a more complex way
to reap qemu-ga's children. Like registering PIDs to be reaped and
having a way to wait for them when returning their exit status to
qemu-ga is necessary. The approach explained above avoids that complexity.
Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
2012-02-28 22:03:03 +08:00
|
|
|
}
|
|
|
|
|
2014-05-02 19:26:30 +08:00
|
|
|
void qmp_guest_suspend_disk(Error **errp)
|
qemu-ga: add guest-suspend-disk
As the command name implies, this command suspends the guest to disk.
The suspend operation is implemented by two functions: bios_supports_mode()
and guest_suspend(). Both functions are generic enough to be used by
other suspend modes (introduced by next commits).
Both functions will try to use the scripts provided by the pm-utils
package if it's available. If it's not available, a manual method,
which consists of directly writing to '/sys/power/state', will be used.
To reap terminated children, a new signal handler is installed in the
parent to catch SIGCHLD signals and a non-blocking call to waitpid()
is done to collect their exit statuses. The statuses, however, are
discarded.
The approach used to query the guest for suspend support deserves some
explanation. It's implemented by bios_supports_mode() and shown below:
qemu-ga
|
create pipe
|
fork()
-----------------
| |
| |
| fork()
| --------------------------
| | |
| | |
| | exec('pm-is-supported')
| |
| wait()
| write exit status to pipe
| exit
|
read pipe
This might look complex, but the resulting code is quite simple.
The purpose of that approach is to allow qemu-ga to reap its children
(semi-)automatically from its SIGCHLD handler.
Implementing this the obvious way, that's, doing the exec() call from
the first child process, would force us to introduce a more complex way
to reap qemu-ga's children. Like registering PIDs to be reaped and
having a way to wait for them when returning their exit status to
qemu-ga is necessary. The approach explained above avoids that complexity.
Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
2012-02-28 22:03:03 +08:00
|
|
|
{
|
2018-06-21 18:21:48 +08:00
|
|
|
guest_suspend(SUSPEND_MODE_DISK, errp);
|
qemu-ga: add guest-suspend-disk
As the command name implies, this command suspends the guest to disk.
The suspend operation is implemented by two functions: bios_supports_mode()
and guest_suspend(). Both functions are generic enough to be used by
other suspend modes (introduced by next commits).
Both functions will try to use the scripts provided by the pm-utils
package if it's available. If it's not available, a manual method,
which consists of directly writing to '/sys/power/state', will be used.
To reap terminated children, a new signal handler is installed in the
parent to catch SIGCHLD signals and a non-blocking call to waitpid()
is done to collect their exit statuses. The statuses, however, are
discarded.
The approach used to query the guest for suspend support deserves some
explanation. It's implemented by bios_supports_mode() and shown below:
qemu-ga
|
create pipe
|
fork()
-----------------
| |
| |
| fork()
| --------------------------
| | |
| | |
| | exec('pm-is-supported')
| |
| wait()
| write exit status to pipe
| exit
|
read pipe
This might look complex, but the resulting code is quite simple.
The purpose of that approach is to allow qemu-ga to reap its children
(semi-)automatically from its SIGCHLD handler.
Implementing this the obvious way, that's, doing the exec() call from
the first child process, would force us to introduce a more complex way
to reap qemu-ga's children. Like registering PIDs to be reaped and
having a way to wait for them when returning their exit status to
qemu-ga is necessary. The approach explained above avoids that complexity.
Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
2012-02-28 22:03:03 +08:00
|
|
|
}
|
|
|
|
|
2014-05-02 19:26:30 +08:00
|
|
|
void qmp_guest_suspend_ram(Error **errp)
|
2012-02-28 22:03:04 +08:00
|
|
|
{
|
2018-06-21 18:21:48 +08:00
|
|
|
guest_suspend(SUSPEND_MODE_RAM, errp);
|
2012-02-28 22:03:04 +08:00
|
|
|
}
|
|
|
|
|
2014-05-02 19:26:30 +08:00
|
|
|
void qmp_guest_suspend_hybrid(Error **errp)
|
2012-02-28 22:03:05 +08:00
|
|
|
{
|
2018-06-21 18:21:48 +08:00
|
|
|
guest_suspend(SUSPEND_MODE_HYBRID, errp);
|
2012-02-28 22:03:05 +08:00
|
|
|
}
|
|
|
|
|
2013-03-07 05:59:30 +08:00
|
|
|
/* 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,
|
2018-09-06 20:51:54 +08:00
|
|
|
char *dirpath, Error **errp)
|
2013-03-07 05:59:30 +08:00
|
|
|
{
|
2018-09-06 20:51:54 +08:00
|
|
|
int fd;
|
|
|
|
int res;
|
2013-03-07 05:59:30 +08:00
|
|
|
int dirfd;
|
2018-09-06 20:51:54 +08:00
|
|
|
static const char fn[] = "online";
|
2013-03-07 05:59:30 +08:00
|
|
|
|
|
|
|
dirfd = open(dirpath, O_RDONLY | O_DIRECTORY);
|
|
|
|
if (dirfd == -1) {
|
|
|
|
error_setg_errno(errp, errno, "open(\"%s\")", dirpath);
|
2018-09-06 20:51:54 +08:00
|
|
|
return;
|
|
|
|
}
|
2013-03-07 05:59:30 +08:00
|
|
|
|
2018-09-06 20:51:54 +08:00
|
|
|
fd = openat(dirfd, fn, sys2vcpu ? O_RDONLY : O_RDWR);
|
|
|
|
if (fd == -1) {
|
|
|
|
if (errno != ENOENT) {
|
|
|
|
error_setg_errno(errp, errno, "open(\"%s/%s\")", dirpath, fn);
|
|
|
|
} else if (sys2vcpu) {
|
|
|
|
vcpu->online = true;
|
|
|
|
vcpu->can_offline = 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 = pread(fd, &status, 1, 0);
|
|
|
|
if (res == -1) {
|
|
|
|
error_setg_errno(errp, errno, "pread(\"%s/%s\")", dirpath, fn);
|
|
|
|
} else if (res == 0) {
|
|
|
|
error_setg(errp, "pread(\"%s/%s\"): unexpected EOF", dirpath,
|
|
|
|
fn);
|
|
|
|
} else if (sys2vcpu) {
|
|
|
|
vcpu->online = (status != '0');
|
|
|
|
vcpu->can_offline = true;
|
|
|
|
} else if (vcpu->online != (status != '0')) {
|
|
|
|
status = '0' + vcpu->online;
|
|
|
|
if (pwrite(fd, &status, 1, 0) == -1) {
|
|
|
|
error_setg_errno(errp, errno, "pwrite(\"%s/%s\")", dirpath,
|
|
|
|
fn);
|
|
|
|
}
|
|
|
|
} /* otherwise pretend successful re-(on|off)-lining */
|
2013-03-07 05:59:30 +08:00
|
|
|
|
2018-09-06 20:51:54 +08:00
|
|
|
res = close(fd);
|
2013-03-07 05:59:30 +08:00
|
|
|
g_assert(res == 0);
|
|
|
|
}
|
|
|
|
|
2018-09-06 20:51:54 +08:00
|
|
|
res = close(dirfd);
|
|
|
|
g_assert(res == 0);
|
2013-03-07 05:59:30 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp)
|
|
|
|
{
|
2021-01-14 06:10:12 +08:00
|
|
|
GuestLogicalProcessorList *head, **tail;
|
qga: Correct loop count in qmp_guest_get_vcpus()
The guest-get-vcpus returns incorrect vcpu info in case we hotunplug vcpus(not
the last one).
e.g.:
A VM has 4 VCPUs: cpu0 + 3 hotunpluggable online vcpus(cpu1, cpu2 and cpu3).
Hotunplug cpu2, Now only cpu0, cpu1 and cpu3 are present & online.
./qmp-shell /tmp/qmp-monitor.sock
(QEMU) query-hotpluggable-cpus
{"return": [
{"props": {"core-id": 0, "thread-id": 0, "socket-id": 3}, "vcpus-count": 1,
"qom-path": "/machine/peripheral/cpu3", "type": "host-x86_64-cpu"},
{"props": {"core-id": 0, "thread-id": 0, "socket-id": 2}, "vcpus-count": 1,
"qom-path": "/machine/peripheral/cpu2", "type": "host-x86_64-cpu"},
{"props": {"core-id": 0, "thread-id": 0, "socket-id": 1}, "vcpus-count": 1,
"qom-path": "/machine/peripheral/cpu1", "type": "host-x86_64-cpu"},
{"props": {"core-id": 0, "thread-id": 0, "socket-id": 0}, "vcpus-count": 1,
"qom-path": "/machine/unattached/device[0]", "type": "host-x86_64-cpu"}
]}
(QEMU) device_del id=cpu2
{"return": {}}
(QEMU) query-hotpluggable-cpus
{"return": [
{"props": {"core-id": 0, "thread-id": 0, "socket-id": 3}, "vcpus-count": 1,
"qom-path": "/machine/peripheral/cpu3", "type": "host-x86_64-cpu"},
{"props": {"core-id": 0, "thread-id": 0, "socket-id": 2}, "vcpus-count": 1,
"type": "host-x86_64-cpu"},
{"props": {"core-id": 0, "thread-id": 0, "socket-id": 1}, "vcpus-count": 1,
"qom-path": "/machine/peripheral/cpu1", "type": "host-x86_64-cpu"},
{"props": {"core-id": 0, "thread-id": 0, "socket-id": 0}, "vcpus-count": 1,
"qom-path": "/machine/unattached/device[0]", "type": "host-x86_64-cpu"}
]}
Before:
./qmp-shell -N /tmp/qmp-ga.sock
Welcome to the QMP low-level shell!
Connected
(QEMU) guest-get-vcpus
{"return": [
{"online": true, "can-offline": false, "logical-id": 0},
{"online": true, "can-offline": true, "logical-id": 1}]}
After:
./qmp-shell -N /tmp/qmp-ga.sock
Welcome to the QMP low-level shell!
Connected
(QEMU) guest-get-vcpus
{"return": [
{"online": true, "can-offline": false, "logical-id": 0},
{"online": true, "can-offline": true, "logical-id": 1},
{"online": true, "can-offline": true, "logical-id": 3}]}
Signed-off-by: Lin Ma <lma@suse.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
*fix build breakage by using PRId64 for sscanf
Signed-off-by: Michael Roth <michael.roth@amd.com>
2021-02-24 13:14:28 +08:00
|
|
|
const char *cpu_dir = "/sys/devices/system/cpu";
|
|
|
|
const gchar *line;
|
|
|
|
g_autoptr(GDir) cpu_gdir = NULL;
|
2013-03-07 05:59:30 +08:00
|
|
|
Error *local_err = NULL;
|
|
|
|
|
|
|
|
head = NULL;
|
2021-01-14 06:10:12 +08:00
|
|
|
tail = &head;
|
qga: Correct loop count in qmp_guest_get_vcpus()
The guest-get-vcpus returns incorrect vcpu info in case we hotunplug vcpus(not
the last one).
e.g.:
A VM has 4 VCPUs: cpu0 + 3 hotunpluggable online vcpus(cpu1, cpu2 and cpu3).
Hotunplug cpu2, Now only cpu0, cpu1 and cpu3 are present & online.
./qmp-shell /tmp/qmp-monitor.sock
(QEMU) query-hotpluggable-cpus
{"return": [
{"props": {"core-id": 0, "thread-id": 0, "socket-id": 3}, "vcpus-count": 1,
"qom-path": "/machine/peripheral/cpu3", "type": "host-x86_64-cpu"},
{"props": {"core-id": 0, "thread-id": 0, "socket-id": 2}, "vcpus-count": 1,
"qom-path": "/machine/peripheral/cpu2", "type": "host-x86_64-cpu"},
{"props": {"core-id": 0, "thread-id": 0, "socket-id": 1}, "vcpus-count": 1,
"qom-path": "/machine/peripheral/cpu1", "type": "host-x86_64-cpu"},
{"props": {"core-id": 0, "thread-id": 0, "socket-id": 0}, "vcpus-count": 1,
"qom-path": "/machine/unattached/device[0]", "type": "host-x86_64-cpu"}
]}
(QEMU) device_del id=cpu2
{"return": {}}
(QEMU) query-hotpluggable-cpus
{"return": [
{"props": {"core-id": 0, "thread-id": 0, "socket-id": 3}, "vcpus-count": 1,
"qom-path": "/machine/peripheral/cpu3", "type": "host-x86_64-cpu"},
{"props": {"core-id": 0, "thread-id": 0, "socket-id": 2}, "vcpus-count": 1,
"type": "host-x86_64-cpu"},
{"props": {"core-id": 0, "thread-id": 0, "socket-id": 1}, "vcpus-count": 1,
"qom-path": "/machine/peripheral/cpu1", "type": "host-x86_64-cpu"},
{"props": {"core-id": 0, "thread-id": 0, "socket-id": 0}, "vcpus-count": 1,
"qom-path": "/machine/unattached/device[0]", "type": "host-x86_64-cpu"}
]}
Before:
./qmp-shell -N /tmp/qmp-ga.sock
Welcome to the QMP low-level shell!
Connected
(QEMU) guest-get-vcpus
{"return": [
{"online": true, "can-offline": false, "logical-id": 0},
{"online": true, "can-offline": true, "logical-id": 1}]}
After:
./qmp-shell -N /tmp/qmp-ga.sock
Welcome to the QMP low-level shell!
Connected
(QEMU) guest-get-vcpus
{"return": [
{"online": true, "can-offline": false, "logical-id": 0},
{"online": true, "can-offline": true, "logical-id": 1},
{"online": true, "can-offline": true, "logical-id": 3}]}
Signed-off-by: Lin Ma <lma@suse.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
*fix build breakage by using PRId64 for sscanf
Signed-off-by: Michael Roth <michael.roth@amd.com>
2021-02-24 13:14:28 +08:00
|
|
|
cpu_gdir = g_dir_open(cpu_dir, 0, NULL);
|
2013-03-07 05:59:30 +08:00
|
|
|
|
qga: Correct loop count in qmp_guest_get_vcpus()
The guest-get-vcpus returns incorrect vcpu info in case we hotunplug vcpus(not
the last one).
e.g.:
A VM has 4 VCPUs: cpu0 + 3 hotunpluggable online vcpus(cpu1, cpu2 and cpu3).
Hotunplug cpu2, Now only cpu0, cpu1 and cpu3 are present & online.
./qmp-shell /tmp/qmp-monitor.sock
(QEMU) query-hotpluggable-cpus
{"return": [
{"props": {"core-id": 0, "thread-id": 0, "socket-id": 3}, "vcpus-count": 1,
"qom-path": "/machine/peripheral/cpu3", "type": "host-x86_64-cpu"},
{"props": {"core-id": 0, "thread-id": 0, "socket-id": 2}, "vcpus-count": 1,
"qom-path": "/machine/peripheral/cpu2", "type": "host-x86_64-cpu"},
{"props": {"core-id": 0, "thread-id": 0, "socket-id": 1}, "vcpus-count": 1,
"qom-path": "/machine/peripheral/cpu1", "type": "host-x86_64-cpu"},
{"props": {"core-id": 0, "thread-id": 0, "socket-id": 0}, "vcpus-count": 1,
"qom-path": "/machine/unattached/device[0]", "type": "host-x86_64-cpu"}
]}
(QEMU) device_del id=cpu2
{"return": {}}
(QEMU) query-hotpluggable-cpus
{"return": [
{"props": {"core-id": 0, "thread-id": 0, "socket-id": 3}, "vcpus-count": 1,
"qom-path": "/machine/peripheral/cpu3", "type": "host-x86_64-cpu"},
{"props": {"core-id": 0, "thread-id": 0, "socket-id": 2}, "vcpus-count": 1,
"type": "host-x86_64-cpu"},
{"props": {"core-id": 0, "thread-id": 0, "socket-id": 1}, "vcpus-count": 1,
"qom-path": "/machine/peripheral/cpu1", "type": "host-x86_64-cpu"},
{"props": {"core-id": 0, "thread-id": 0, "socket-id": 0}, "vcpus-count": 1,
"qom-path": "/machine/unattached/device[0]", "type": "host-x86_64-cpu"}
]}
Before:
./qmp-shell -N /tmp/qmp-ga.sock
Welcome to the QMP low-level shell!
Connected
(QEMU) guest-get-vcpus
{"return": [
{"online": true, "can-offline": false, "logical-id": 0},
{"online": true, "can-offline": true, "logical-id": 1}]}
After:
./qmp-shell -N /tmp/qmp-ga.sock
Welcome to the QMP low-level shell!
Connected
(QEMU) guest-get-vcpus
{"return": [
{"online": true, "can-offline": false, "logical-id": 0},
{"online": true, "can-offline": true, "logical-id": 1},
{"online": true, "can-offline": true, "logical-id": 3}]}
Signed-off-by: Lin Ma <lma@suse.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
*fix build breakage by using PRId64 for sscanf
Signed-off-by: Michael Roth <michael.roth@amd.com>
2021-02-24 13:14:28 +08:00
|
|
|
if (cpu_gdir == NULL) {
|
|
|
|
error_setg_errno(errp, errno, "failed to list entries: %s", cpu_dir);
|
|
|
|
return NULL;
|
|
|
|
}
|
2018-09-06 20:51:54 +08:00
|
|
|
|
qga: Correct loop count in qmp_guest_get_vcpus()
The guest-get-vcpus returns incorrect vcpu info in case we hotunplug vcpus(not
the last one).
e.g.:
A VM has 4 VCPUs: cpu0 + 3 hotunpluggable online vcpus(cpu1, cpu2 and cpu3).
Hotunplug cpu2, Now only cpu0, cpu1 and cpu3 are present & online.
./qmp-shell /tmp/qmp-monitor.sock
(QEMU) query-hotpluggable-cpus
{"return": [
{"props": {"core-id": 0, "thread-id": 0, "socket-id": 3}, "vcpus-count": 1,
"qom-path": "/machine/peripheral/cpu3", "type": "host-x86_64-cpu"},
{"props": {"core-id": 0, "thread-id": 0, "socket-id": 2}, "vcpus-count": 1,
"qom-path": "/machine/peripheral/cpu2", "type": "host-x86_64-cpu"},
{"props": {"core-id": 0, "thread-id": 0, "socket-id": 1}, "vcpus-count": 1,
"qom-path": "/machine/peripheral/cpu1", "type": "host-x86_64-cpu"},
{"props": {"core-id": 0, "thread-id": 0, "socket-id": 0}, "vcpus-count": 1,
"qom-path": "/machine/unattached/device[0]", "type": "host-x86_64-cpu"}
]}
(QEMU) device_del id=cpu2
{"return": {}}
(QEMU) query-hotpluggable-cpus
{"return": [
{"props": {"core-id": 0, "thread-id": 0, "socket-id": 3}, "vcpus-count": 1,
"qom-path": "/machine/peripheral/cpu3", "type": "host-x86_64-cpu"},
{"props": {"core-id": 0, "thread-id": 0, "socket-id": 2}, "vcpus-count": 1,
"type": "host-x86_64-cpu"},
{"props": {"core-id": 0, "thread-id": 0, "socket-id": 1}, "vcpus-count": 1,
"qom-path": "/machine/peripheral/cpu1", "type": "host-x86_64-cpu"},
{"props": {"core-id": 0, "thread-id": 0, "socket-id": 0}, "vcpus-count": 1,
"qom-path": "/machine/unattached/device[0]", "type": "host-x86_64-cpu"}
]}
Before:
./qmp-shell -N /tmp/qmp-ga.sock
Welcome to the QMP low-level shell!
Connected
(QEMU) guest-get-vcpus
{"return": [
{"online": true, "can-offline": false, "logical-id": 0},
{"online": true, "can-offline": true, "logical-id": 1}]}
After:
./qmp-shell -N /tmp/qmp-ga.sock
Welcome to the QMP low-level shell!
Connected
(QEMU) guest-get-vcpus
{"return": [
{"online": true, "can-offline": false, "logical-id": 0},
{"online": true, "can-offline": true, "logical-id": 1},
{"online": true, "can-offline": true, "logical-id": 3}]}
Signed-off-by: Lin Ma <lma@suse.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
*fix build breakage by using PRId64 for sscanf
Signed-off-by: Michael Roth <michael.roth@amd.com>
2021-02-24 13:14:28 +08:00
|
|
|
while (local_err == NULL && (line = g_dir_read_name(cpu_gdir)) != NULL) {
|
|
|
|
GuestLogicalProcessor *vcpu;
|
|
|
|
int64_t id;
|
|
|
|
if (sscanf(line, "cpu%" PRId64, &id)) {
|
|
|
|
g_autofree char *path = g_strdup_printf("/sys/devices/system/cpu/"
|
|
|
|
"cpu%" PRId64 "/", id);
|
2018-09-06 20:51:54 +08:00
|
|
|
vcpu = g_malloc0(sizeof *vcpu);
|
|
|
|
vcpu->logical_id = id;
|
|
|
|
vcpu->has_can_offline = true; /* lolspeak ftw */
|
|
|
|
transfer_vcpu(vcpu, true, path, &local_err);
|
2021-01-14 06:10:12 +08:00
|
|
|
QAPI_LIST_APPEND(tail, vcpu);
|
2018-09-06 20:51:54 +08:00
|
|
|
}
|
2013-03-07 05:59:30 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (local_err == NULL) {
|
|
|
|
/* there's no guest with zero VCPUs */
|
|
|
|
g_assert(head != NULL);
|
|
|
|
return head;
|
|
|
|
}
|
|
|
|
|
|
|
|
qapi_free_GuestLogicalProcessorList(head);
|
|
|
|
error_propagate(errp, local_err);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-03-07 05:59:31 +08:00
|
|
|
int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp)
|
|
|
|
{
|
|
|
|
int64_t processed;
|
|
|
|
Error *local_err = NULL;
|
|
|
|
|
|
|
|
processed = 0;
|
|
|
|
while (vcpus != NULL) {
|
2018-09-06 20:51:54 +08:00
|
|
|
char *path = g_strdup_printf("/sys/devices/system/cpu/cpu%" PRId64 "/",
|
|
|
|
vcpus->value->logical_id);
|
|
|
|
|
|
|
|
transfer_vcpu(vcpus->value, false, path, &local_err);
|
|
|
|
g_free(path);
|
2013-03-07 05:59:31 +08:00
|
|
|
if (local_err != NULL) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
++processed;
|
|
|
|
vcpus = vcpus->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (local_err != NULL) {
|
|
|
|
if (processed == 0) {
|
|
|
|
error_propagate(errp, local_err);
|
|
|
|
} else {
|
|
|
|
error_free(local_err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return processed;
|
|
|
|
}
|
2022-10-17 15:28:23 +08:00
|
|
|
#endif /* __linux__ */
|
2013-03-07 05:59:31 +08:00
|
|
|
|
2022-10-17 15:28:23 +08:00
|
|
|
#if defined(__linux__) || defined(__FreeBSD__)
|
qga: add guest-set-user-password command
Add a new 'guest-set-user-password' command for changing the password
of guest OS user accounts. This command is needed to enable OpenStack
to support its API for changing the admin password of guests running
on KVM/QEMU. It is not practical to provide a command at the QEMU
level explicitly targetting administrator account password change
only, since different guest OS have different names for the admin
account. While UNIX systems use 'root', Windows systems typically
use 'Administrator' and even that can be renamed. Higher level apps
like OpenStack have the ability to figure out the correct admin
account name since they have info that QEMU/libvirt do not.
The command accepts either the clear text password string, encoded
in base64 to make it 8-bit safe in JSON:
$ echo -n "123456" | base64
MTIzNDU2
$ virsh -c qemu:///system qemu-agent-command f21x86_64 \
'{ "execute": "guest-set-user-password",
"arguments": { "crypted": false,
"username": "root",
"password": "MTIzNDU2" } }'
{"return":{}}
Or a password that has already been run though a crypt(3) like
algorithm appropriate for the guest, again then base64 encoded:
$ echo -n '$6$n01A2Tau$e...snip...DfMOP7of9AJ1I8q0' | base64
JDYkb...snip...YT2Ey
$ virsh -c qemu:///system qemu-agent-command f21x86_64 \
'{ "execute": "guest-set-user-password",
"arguments": { "crypted": true,
"username": "root",
"password": "JDYkb...snip...YT2Ey" } }'
NB windows support is desirable, but not implemented in this
patch.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2015-02-11 19:26:12 +08:00
|
|
|
void qmp_guest_set_user_password(const char *username,
|
|
|
|
const char *password,
|
|
|
|
bool crypted,
|
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
Error *local_err = NULL;
|
|
|
|
char *passwd_path = NULL;
|
|
|
|
pid_t pid;
|
|
|
|
int status;
|
|
|
|
int datafd[2] = { -1, -1 };
|
|
|
|
char *rawpasswddata = NULL;
|
|
|
|
size_t rawpasswdlen;
|
|
|
|
char *chpasswddata = NULL;
|
|
|
|
size_t chpasswdlen;
|
|
|
|
|
2015-11-23 23:37:07 +08:00
|
|
|
rawpasswddata = (char *)qbase64_decode(password, -1, &rawpasswdlen, errp);
|
|
|
|
if (!rawpasswddata) {
|
|
|
|
return;
|
|
|
|
}
|
qga: add guest-set-user-password command
Add a new 'guest-set-user-password' command for changing the password
of guest OS user accounts. This command is needed to enable OpenStack
to support its API for changing the admin password of guests running
on KVM/QEMU. It is not practical to provide a command at the QEMU
level explicitly targetting administrator account password change
only, since different guest OS have different names for the admin
account. While UNIX systems use 'root', Windows systems typically
use 'Administrator' and even that can be renamed. Higher level apps
like OpenStack have the ability to figure out the correct admin
account name since they have info that QEMU/libvirt do not.
The command accepts either the clear text password string, encoded
in base64 to make it 8-bit safe in JSON:
$ echo -n "123456" | base64
MTIzNDU2
$ virsh -c qemu:///system qemu-agent-command f21x86_64 \
'{ "execute": "guest-set-user-password",
"arguments": { "crypted": false,
"username": "root",
"password": "MTIzNDU2" } }'
{"return":{}}
Or a password that has already been run though a crypt(3) like
algorithm appropriate for the guest, again then base64 encoded:
$ echo -n '$6$n01A2Tau$e...snip...DfMOP7of9AJ1I8q0' | base64
JDYkb...snip...YT2Ey
$ virsh -c qemu:///system qemu-agent-command f21x86_64 \
'{ "execute": "guest-set-user-password",
"arguments": { "crypted": true,
"username": "root",
"password": "JDYkb...snip...YT2Ey" } }'
NB windows support is desirable, but not implemented in this
patch.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2015-02-11 19:26:12 +08:00
|
|
|
rawpasswddata = g_renew(char, rawpasswddata, rawpasswdlen + 1);
|
|
|
|
rawpasswddata[rawpasswdlen] = '\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;
|
|
|
|
}
|
|
|
|
|
2022-10-17 15:28:23 +08:00
|
|
|
#ifdef __FreeBSD__
|
|
|
|
chpasswddata = g_strdup(rawpasswddata);
|
|
|
|
passwd_path = g_find_program_in_path("pw");
|
|
|
|
#else
|
qga: add guest-set-user-password command
Add a new 'guest-set-user-password' command for changing the password
of guest OS user accounts. This command is needed to enable OpenStack
to support its API for changing the admin password of guests running
on KVM/QEMU. It is not practical to provide a command at the QEMU
level explicitly targetting administrator account password change
only, since different guest OS have different names for the admin
account. While UNIX systems use 'root', Windows systems typically
use 'Administrator' and even that can be renamed. Higher level apps
like OpenStack have the ability to figure out the correct admin
account name since they have info that QEMU/libvirt do not.
The command accepts either the clear text password string, encoded
in base64 to make it 8-bit safe in JSON:
$ echo -n "123456" | base64
MTIzNDU2
$ virsh -c qemu:///system qemu-agent-command f21x86_64 \
'{ "execute": "guest-set-user-password",
"arguments": { "crypted": false,
"username": "root",
"password": "MTIzNDU2" } }'
{"return":{}}
Or a password that has already been run though a crypt(3) like
algorithm appropriate for the guest, again then base64 encoded:
$ echo -n '$6$n01A2Tau$e...snip...DfMOP7of9AJ1I8q0' | base64
JDYkb...snip...YT2Ey
$ virsh -c qemu:///system qemu-agent-command f21x86_64 \
'{ "execute": "guest-set-user-password",
"arguments": { "crypted": true,
"username": "root",
"password": "JDYkb...snip...YT2Ey" } }'
NB windows support is desirable, but not implemented in this
patch.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2015-02-11 19:26:12 +08:00
|
|
|
chpasswddata = g_strdup_printf("%s:%s\n", username, rawpasswddata);
|
|
|
|
passwd_path = g_find_program_in_path("chpasswd");
|
2022-10-17 15:28:23 +08:00
|
|
|
#endif
|
|
|
|
|
|
|
|
chpasswdlen = strlen(chpasswddata);
|
qga: add guest-set-user-password command
Add a new 'guest-set-user-password' command for changing the password
of guest OS user accounts. This command is needed to enable OpenStack
to support its API for changing the admin password of guests running
on KVM/QEMU. It is not practical to provide a command at the QEMU
level explicitly targetting administrator account password change
only, since different guest OS have different names for the admin
account. While UNIX systems use 'root', Windows systems typically
use 'Administrator' and even that can be renamed. Higher level apps
like OpenStack have the ability to figure out the correct admin
account name since they have info that QEMU/libvirt do not.
The command accepts either the clear text password string, encoded
in base64 to make it 8-bit safe in JSON:
$ echo -n "123456" | base64
MTIzNDU2
$ virsh -c qemu:///system qemu-agent-command f21x86_64 \
'{ "execute": "guest-set-user-password",
"arguments": { "crypted": false,
"username": "root",
"password": "MTIzNDU2" } }'
{"return":{}}
Or a password that has already been run though a crypt(3) like
algorithm appropriate for the guest, again then base64 encoded:
$ echo -n '$6$n01A2Tau$e...snip...DfMOP7of9AJ1I8q0' | base64
JDYkb...snip...YT2Ey
$ virsh -c qemu:///system qemu-agent-command f21x86_64 \
'{ "execute": "guest-set-user-password",
"arguments": { "crypted": true,
"username": "root",
"password": "JDYkb...snip...YT2Ey" } }'
NB windows support is desirable, but not implemented in this
patch.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2015-02-11 19:26:12 +08:00
|
|
|
|
|
|
|
if (!passwd_path) {
|
|
|
|
error_setg(errp, "cannot find 'passwd' program in PATH");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2022-04-22 18:46:36 +08:00
|
|
|
if (!g_unix_open_pipe(datafd, FD_CLOEXEC, NULL)) {
|
qga: add guest-set-user-password command
Add a new 'guest-set-user-password' command for changing the password
of guest OS user accounts. This command is needed to enable OpenStack
to support its API for changing the admin password of guests running
on KVM/QEMU. It is not practical to provide a command at the QEMU
level explicitly targetting administrator account password change
only, since different guest OS have different names for the admin
account. While UNIX systems use 'root', Windows systems typically
use 'Administrator' and even that can be renamed. Higher level apps
like OpenStack have the ability to figure out the correct admin
account name since they have info that QEMU/libvirt do not.
The command accepts either the clear text password string, encoded
in base64 to make it 8-bit safe in JSON:
$ echo -n "123456" | base64
MTIzNDU2
$ virsh -c qemu:///system qemu-agent-command f21x86_64 \
'{ "execute": "guest-set-user-password",
"arguments": { "crypted": false,
"username": "root",
"password": "MTIzNDU2" } }'
{"return":{}}
Or a password that has already been run though a crypt(3) like
algorithm appropriate for the guest, again then base64 encoded:
$ echo -n '$6$n01A2Tau$e...snip...DfMOP7of9AJ1I8q0' | base64
JDYkb...snip...YT2Ey
$ virsh -c qemu:///system qemu-agent-command f21x86_64 \
'{ "execute": "guest-set-user-password",
"arguments": { "crypted": true,
"username": "root",
"password": "JDYkb...snip...YT2Ey" } }'
NB windows support is desirable, but not implemented in this
patch.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2015-02-11 19:26:12 +08:00
|
|
|
error_setg(errp, "cannot create pipe FDs");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
pid = fork();
|
|
|
|
if (pid == 0) {
|
|
|
|
close(datafd[1]);
|
|
|
|
/* child */
|
|
|
|
setsid();
|
|
|
|
dup2(datafd[0], 0);
|
|
|
|
reopen_fd_to_null(1);
|
|
|
|
reopen_fd_to_null(2);
|
|
|
|
|
2022-10-17 15:28:23 +08:00
|
|
|
#ifdef __FreeBSD__
|
|
|
|
const char *h_arg;
|
|
|
|
h_arg = (crypted) ? "-H" : "-h";
|
|
|
|
execl(passwd_path, "pw", "usermod", "-n", username, h_arg, "0", NULL);
|
|
|
|
#else
|
qga: add guest-set-user-password command
Add a new 'guest-set-user-password' command for changing the password
of guest OS user accounts. This command is needed to enable OpenStack
to support its API for changing the admin password of guests running
on KVM/QEMU. It is not practical to provide a command at the QEMU
level explicitly targetting administrator account password change
only, since different guest OS have different names for the admin
account. While UNIX systems use 'root', Windows systems typically
use 'Administrator' and even that can be renamed. Higher level apps
like OpenStack have the ability to figure out the correct admin
account name since they have info that QEMU/libvirt do not.
The command accepts either the clear text password string, encoded
in base64 to make it 8-bit safe in JSON:
$ echo -n "123456" | base64
MTIzNDU2
$ virsh -c qemu:///system qemu-agent-command f21x86_64 \
'{ "execute": "guest-set-user-password",
"arguments": { "crypted": false,
"username": "root",
"password": "MTIzNDU2" } }'
{"return":{}}
Or a password that has already been run though a crypt(3) like
algorithm appropriate for the guest, again then base64 encoded:
$ echo -n '$6$n01A2Tau$e...snip...DfMOP7of9AJ1I8q0' | base64
JDYkb...snip...YT2Ey
$ virsh -c qemu:///system qemu-agent-command f21x86_64 \
'{ "execute": "guest-set-user-password",
"arguments": { "crypted": true,
"username": "root",
"password": "JDYkb...snip...YT2Ey" } }'
NB windows support is desirable, but not implemented in this
patch.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2015-02-11 19:26:12 +08:00
|
|
|
if (crypted) {
|
2022-03-23 23:57:41 +08:00
|
|
|
execl(passwd_path, "chpasswd", "-e", NULL);
|
qga: add guest-set-user-password command
Add a new 'guest-set-user-password' command for changing the password
of guest OS user accounts. This command is needed to enable OpenStack
to support its API for changing the admin password of guests running
on KVM/QEMU. It is not practical to provide a command at the QEMU
level explicitly targetting administrator account password change
only, since different guest OS have different names for the admin
account. While UNIX systems use 'root', Windows systems typically
use 'Administrator' and even that can be renamed. Higher level apps
like OpenStack have the ability to figure out the correct admin
account name since they have info that QEMU/libvirt do not.
The command accepts either the clear text password string, encoded
in base64 to make it 8-bit safe in JSON:
$ echo -n "123456" | base64
MTIzNDU2
$ virsh -c qemu:///system qemu-agent-command f21x86_64 \
'{ "execute": "guest-set-user-password",
"arguments": { "crypted": false,
"username": "root",
"password": "MTIzNDU2" } }'
{"return":{}}
Or a password that has already been run though a crypt(3) like
algorithm appropriate for the guest, again then base64 encoded:
$ echo -n '$6$n01A2Tau$e...snip...DfMOP7of9AJ1I8q0' | base64
JDYkb...snip...YT2Ey
$ virsh -c qemu:///system qemu-agent-command f21x86_64 \
'{ "execute": "guest-set-user-password",
"arguments": { "crypted": true,
"username": "root",
"password": "JDYkb...snip...YT2Ey" } }'
NB windows support is desirable, but not implemented in this
patch.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2015-02-11 19:26:12 +08:00
|
|
|
} else {
|
2022-03-23 23:57:41 +08:00
|
|
|
execl(passwd_path, "chpasswd", NULL);
|
qga: add guest-set-user-password command
Add a new 'guest-set-user-password' command for changing the password
of guest OS user accounts. This command is needed to enable OpenStack
to support its API for changing the admin password of guests running
on KVM/QEMU. It is not practical to provide a command at the QEMU
level explicitly targetting administrator account password change
only, since different guest OS have different names for the admin
account. While UNIX systems use 'root', Windows systems typically
use 'Administrator' and even that can be renamed. Higher level apps
like OpenStack have the ability to figure out the correct admin
account name since they have info that QEMU/libvirt do not.
The command accepts either the clear text password string, encoded
in base64 to make it 8-bit safe in JSON:
$ echo -n "123456" | base64
MTIzNDU2
$ virsh -c qemu:///system qemu-agent-command f21x86_64 \
'{ "execute": "guest-set-user-password",
"arguments": { "crypted": false,
"username": "root",
"password": "MTIzNDU2" } }'
{"return":{}}
Or a password that has already been run though a crypt(3) like
algorithm appropriate for the guest, again then base64 encoded:
$ echo -n '$6$n01A2Tau$e...snip...DfMOP7of9AJ1I8q0' | base64
JDYkb...snip...YT2Ey
$ virsh -c qemu:///system qemu-agent-command f21x86_64 \
'{ "execute": "guest-set-user-password",
"arguments": { "crypted": true,
"username": "root",
"password": "JDYkb...snip...YT2Ey" } }'
NB windows support is desirable, but not implemented in this
patch.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2015-02-11 19:26:12 +08:00
|
|
|
}
|
2022-10-17 15:28:23 +08:00
|
|
|
#endif
|
qga: add guest-set-user-password command
Add a new 'guest-set-user-password' command for changing the password
of guest OS user accounts. This command is needed to enable OpenStack
to support its API for changing the admin password of guests running
on KVM/QEMU. It is not practical to provide a command at the QEMU
level explicitly targetting administrator account password change
only, since different guest OS have different names for the admin
account. While UNIX systems use 'root', Windows systems typically
use 'Administrator' and even that can be renamed. Higher level apps
like OpenStack have the ability to figure out the correct admin
account name since they have info that QEMU/libvirt do not.
The command accepts either the clear text password string, encoded
in base64 to make it 8-bit safe in JSON:
$ echo -n "123456" | base64
MTIzNDU2
$ virsh -c qemu:///system qemu-agent-command f21x86_64 \
'{ "execute": "guest-set-user-password",
"arguments": { "crypted": false,
"username": "root",
"password": "MTIzNDU2" } }'
{"return":{}}
Or a password that has already been run though a crypt(3) like
algorithm appropriate for the guest, again then base64 encoded:
$ echo -n '$6$n01A2Tau$e...snip...DfMOP7of9AJ1I8q0' | base64
JDYkb...snip...YT2Ey
$ virsh -c qemu:///system qemu-agent-command f21x86_64 \
'{ "execute": "guest-set-user-password",
"arguments": { "crypted": true,
"username": "root",
"password": "JDYkb...snip...YT2Ey" } }'
NB windows support is desirable, but not implemented in this
patch.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2015-02-11 19:26:12 +08:00
|
|
|
_exit(EXIT_FAILURE);
|
|
|
|
} else if (pid < 0) {
|
|
|
|
error_setg_errno(errp, errno, "failed to create child process");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
close(datafd[0]);
|
|
|
|
datafd[0] = -1;
|
|
|
|
|
|
|
|
if (qemu_write_full(datafd[1], chpasswddata, chpasswdlen) != chpasswdlen) {
|
|
|
|
error_setg_errno(errp, errno, "cannot write new account password");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
close(datafd[1]);
|
|
|
|
datafd[1] = -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] != -1) {
|
|
|
|
close(datafd[0]);
|
|
|
|
}
|
|
|
|
if (datafd[1] != -1) {
|
|
|
|
close(datafd[1]);
|
|
|
|
}
|
|
|
|
}
|
2022-10-17 15:28:23 +08:00
|
|
|
#else /* __linux__ || __FreeBSD__ */
|
|
|
|
void qmp_guest_set_user_password(const char *username,
|
|
|
|
const char *password,
|
|
|
|
bool crypted,
|
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
error_setg(errp, QERR_UNSUPPORTED);
|
|
|
|
}
|
|
|
|
#endif /* __linux__ || __FreeBSD__ */
|
qga: add guest-set-user-password command
Add a new 'guest-set-user-password' command for changing the password
of guest OS user accounts. This command is needed to enable OpenStack
to support its API for changing the admin password of guests running
on KVM/QEMU. It is not practical to provide a command at the QEMU
level explicitly targetting administrator account password change
only, since different guest OS have different names for the admin
account. While UNIX systems use 'root', Windows systems typically
use 'Administrator' and even that can be renamed. Higher level apps
like OpenStack have the ability to figure out the correct admin
account name since they have info that QEMU/libvirt do not.
The command accepts either the clear text password string, encoded
in base64 to make it 8-bit safe in JSON:
$ echo -n "123456" | base64
MTIzNDU2
$ virsh -c qemu:///system qemu-agent-command f21x86_64 \
'{ "execute": "guest-set-user-password",
"arguments": { "crypted": false,
"username": "root",
"password": "MTIzNDU2" } }'
{"return":{}}
Or a password that has already been run though a crypt(3) like
algorithm appropriate for the guest, again then base64 encoded:
$ echo -n '$6$n01A2Tau$e...snip...DfMOP7of9AJ1I8q0' | base64
JDYkb...snip...YT2Ey
$ virsh -c qemu:///system qemu-agent-command f21x86_64 \
'{ "execute": "guest-set-user-password",
"arguments": { "crypted": true,
"username": "root",
"password": "JDYkb...snip...YT2Ey" } }'
NB windows support is desirable, but not implemented in this
patch.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2015-02-11 19:26:12 +08:00
|
|
|
|
2022-10-17 15:28:23 +08:00
|
|
|
#ifdef __linux__
|
2015-01-22 10:40:03 +08:00
|
|
|
static void ga_read_sysfs_file(int dirfd, const char *pathname, char *buf,
|
|
|
|
int size, Error **errp)
|
|
|
|
{
|
|
|
|
int fd;
|
|
|
|
int res;
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
fd = openat(dirfd, pathname, O_RDONLY);
|
|
|
|
if (fd == -1) {
|
|
|
|
error_setg_errno(errp, errno, "open sysfs file \"%s\"", pathname);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
res = pread(fd, buf, size, 0);
|
|
|
|
if (res == -1) {
|
|
|
|
error_setg_errno(errp, errno, "pread sysfs file \"%s\"", pathname);
|
|
|
|
} else if (res == 0) {
|
|
|
|
error_setg(errp, "pread sysfs file \"%s\": unexpected EOF", pathname);
|
|
|
|
}
|
|
|
|
close(fd);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ga_write_sysfs_file(int dirfd, const char *pathname,
|
|
|
|
const char *buf, int size, Error **errp)
|
|
|
|
{
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
fd = openat(dirfd, pathname, O_WRONLY);
|
|
|
|
if (fd == -1) {
|
|
|
|
error_setg_errno(errp, errno, "open sysfs file \"%s\"", pathname);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pwrite(fd, buf, size, 0) == -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 accessed:
|
|
|
|
* - 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 accessed:
|
|
|
|
* - 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 sys2memblk,
|
|
|
|
GuestMemoryBlockResponse *result,
|
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
char *dirpath;
|
|
|
|
int dirfd;
|
|
|
|
char *status;
|
|
|
|
Error *local_err = NULL;
|
|
|
|
|
|
|
|
if (!sys2memblk) {
|
|
|
|
DIR *dp;
|
|
|
|
|
|
|
|
if (!result) {
|
|
|
|
error_setg(errp, "Internal error, 'result' should not be NULL");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
errno = 0;
|
|
|
|
dp = 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?
|
|
|
|
*/
|
2017-04-08 06:20:15 +08:00
|
|
|
if (!dp) {
|
|
|
|
if (errno == ENOENT) {
|
|
|
|
result->response =
|
|
|
|
GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_NOT_SUPPORTED;
|
|
|
|
}
|
2015-01-22 10:40:03 +08:00
|
|
|
goto out1;
|
|
|
|
}
|
|
|
|
closedir(dp);
|
|
|
|
}
|
|
|
|
|
|
|
|
dirpath = g_strdup_printf("/sys/devices/system/memory/memory%" PRId64 "/",
|
|
|
|
mem_blk->phys_index);
|
|
|
|
dirfd = open(dirpath, O_RDONLY | O_DIRECTORY);
|
|
|
|
if (dirfd == -1) {
|
|
|
|
if (sys2memblk) {
|
|
|
|
error_setg_errno(errp, errno, "open(\"%s\")", dirpath);
|
|
|
|
} else {
|
|
|
|
if (errno == ENOENT) {
|
|
|
|
result->response = GUEST_MEMORY_BLOCK_RESPONSE_TYPE_NOT_FOUND;
|
|
|
|
} else {
|
|
|
|
result->response =
|
|
|
|
GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_FAILED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
g_free(dirpath);
|
|
|
|
goto out1;
|
|
|
|
}
|
|
|
|
g_free(dirpath);
|
|
|
|
|
|
|
|
status = 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 == ENOENT) {
|
|
|
|
error_free(local_err);
|
|
|
|
if (sys2memblk) {
|
|
|
|
mem_blk->online = true;
|
|
|
|
mem_blk->can_offline = false;
|
|
|
|
} else if (!mem_blk->online) {
|
|
|
|
result->response =
|
|
|
|
GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_NOT_SUPPORTED;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (sys2memblk) {
|
|
|
|
error_propagate(errp, local_err);
|
|
|
|
} else {
|
2020-06-30 17:03:33 +08:00
|
|
|
error_free(local_err);
|
2015-01-22 10:40:03 +08:00
|
|
|
result->response =
|
|
|
|
GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_FAILED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
goto out2;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sys2memblk) {
|
|
|
|
char removable = '0';
|
|
|
|
|
|
|
|
mem_blk->online = (strncmp(status, "online", 6) == 0);
|
|
|
|
|
|
|
|
ga_read_sysfs_file(dirfd, "removable", &removable, 1, &local_err);
|
|
|
|
if (local_err) {
|
2015-09-09 05:45:14 +08:00
|
|
|
/* if no 'removable' file, it doesn't support offline mem blk */
|
2015-01-22 10:40:03 +08:00
|
|
|
if (errno == ENOENT) {
|
|
|
|
error_free(local_err);
|
|
|
|
mem_blk->can_offline = false;
|
|
|
|
} else {
|
|
|
|
error_propagate(errp, local_err);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
mem_blk->can_offline = (removable != '0');
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (mem_blk->online != (strncmp(status, "online", 6) == 0)) {
|
2017-05-26 18:13:37 +08:00
|
|
|
const char *new_state = mem_blk->online ? "online" : "offline";
|
2015-01-22 10:40:03 +08:00
|
|
|
|
|
|
|
ga_write_sysfs_file(dirfd, "state", new_state, strlen(new_state),
|
|
|
|
&local_err);
|
|
|
|
if (local_err) {
|
|
|
|
error_free(local_err);
|
|
|
|
result->response =
|
|
|
|
GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_FAILED;
|
|
|
|
goto out2;
|
|
|
|
}
|
|
|
|
|
|
|
|
result->response = GUEST_MEMORY_BLOCK_RESPONSE_TYPE_SUCCESS;
|
|
|
|
result->has_error_code = 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 = true;
|
|
|
|
result->error_code = errno;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-22 10:40:02 +08:00
|
|
|
GuestMemoryBlockList *qmp_guest_get_memory_blocks(Error **errp)
|
|
|
|
{
|
2021-01-14 06:10:12 +08:00
|
|
|
GuestMemoryBlockList *head, **tail;
|
2015-01-22 10:40:03 +08:00
|
|
|
Error *local_err = NULL;
|
|
|
|
struct dirent *de;
|
|
|
|
DIR *dp;
|
|
|
|
|
|
|
|
head = NULL;
|
2021-01-14 06:10:12 +08:00
|
|
|
tail = &head;
|
2015-01-22 10:40:03 +08:00
|
|
|
|
|
|
|
dp = opendir("/sys/devices/system/memory/");
|
|
|
|
if (!dp) {
|
2015-10-20 05:38:25 +08:00
|
|
|
/* 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 != ENOENT) {
|
|
|
|
error_setg_errno(errp, errno, "Can't open directory"
|
2015-12-18 23:35:19 +08:00
|
|
|
"\"/sys/devices/system/memory/\"");
|
2015-10-20 05:38:25 +08:00
|
|
|
}
|
2015-01-22 10:40:03 +08:00
|
|
|
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, which
|
|
|
|
* allows discontinuous memory ranges (ex. NUMA), so here we should
|
|
|
|
* traverse the memory block directory.
|
|
|
|
*/
|
|
|
|
while ((de = readdir(dp)) != NULL) {
|
|
|
|
GuestMemoryBlock *mem_blk;
|
|
|
|
|
|
|
|
if ((strncmp(de->d_name, "memory", 6) != 0) ||
|
|
|
|
!(de->d_type & DT_DIR)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
mem_blk = g_malloc0(sizeof *mem_blk);
|
|
|
|
/* The d_name is "memoryXXX", phys_index is block id, same as XXX */
|
|
|
|
mem_blk->phys_index = strtoul(&de->d_name[6], NULL, 10);
|
|
|
|
mem_blk->has_can_offline = true; /* lolspeak ftw */
|
|
|
|
transfer_memory_block(mem_blk, true, NULL, &local_err);
|
2020-04-22 21:07:18 +08:00
|
|
|
if (local_err) {
|
|
|
|
break;
|
|
|
|
}
|
2015-01-22 10:40:03 +08:00
|
|
|
|
2021-01-14 06:10:12 +08:00
|
|
|
QAPI_LIST_APPEND(tail, mem_blk);
|
2015-01-22 10:40:03 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
closedir(dp);
|
|
|
|
if (local_err == NULL) {
|
|
|
|
/* there's no guest with zero memory blocks */
|
|
|
|
if (head == NULL) {
|
|
|
|
error_setg(errp, "guest reported zero memory blocks!");
|
|
|
|
}
|
|
|
|
return head;
|
|
|
|
}
|
|
|
|
|
|
|
|
qapi_free_GuestMemoryBlockList(head);
|
|
|
|
error_propagate(errp, local_err);
|
2015-01-22 10:40:02 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
GuestMemoryBlockResponseList *
|
|
|
|
qmp_guest_set_memory_blocks(GuestMemoryBlockList *mem_blks, Error **errp)
|
|
|
|
{
|
2021-01-14 06:10:12 +08:00
|
|
|
GuestMemoryBlockResponseList *head, **tail;
|
2015-01-22 10:40:04 +08:00
|
|
|
Error *local_err = NULL;
|
|
|
|
|
|
|
|
head = NULL;
|
2021-01-14 06:10:12 +08:00
|
|
|
tail = &head;
|
2015-01-22 10:40:04 +08:00
|
|
|
|
|
|
|
while (mem_blks != NULL) {
|
|
|
|
GuestMemoryBlockResponse *result;
|
|
|
|
GuestMemoryBlock *current_mem_blk = mem_blks->value;
|
|
|
|
|
|
|
|
result = g_malloc0(sizeof(*result));
|
|
|
|
result->phys_index = current_mem_blk->phys_index;
|
|
|
|
transfer_memory_block(current_mem_blk, false, result, &local_err);
|
|
|
|
if (local_err) { /* should never happen */
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2021-01-14 06:10:12 +08:00
|
|
|
QAPI_LIST_APPEND(tail, result);
|
2015-01-22 10:40:04 +08:00
|
|
|
mem_blks = mem_blks->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
return head;
|
|
|
|
err:
|
|
|
|
qapi_free_GuestMemoryBlockResponseList(head);
|
|
|
|
error_propagate(errp, local_err);
|
2015-01-22 10:40:02 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
GuestMemoryBlockInfo *qmp_guest_get_memory_block_info(Error **errp)
|
|
|
|
{
|
2015-01-22 10:40:05 +08:00
|
|
|
Error *local_err = NULL;
|
|
|
|
char *dirpath;
|
|
|
|
int dirfd;
|
|
|
|
char *buf;
|
|
|
|
GuestMemoryBlockInfo *info;
|
|
|
|
|
|
|
|
dirpath = g_strdup_printf("/sys/devices/system/memory/");
|
|
|
|
dirfd = open(dirpath, O_RDONLY | O_DIRECTORY);
|
|
|
|
if (dirfd == -1) {
|
|
|
|
error_setg_errno(errp, errno, "open(\"%s\")", dirpath);
|
|
|
|
g_free(dirpath);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
g_free(dirpath);
|
|
|
|
|
|
|
|
buf = g_malloc0(20);
|
|
|
|
ga_read_sysfs_file(dirfd, "block_size_bytes", buf, 20, &local_err);
|
2015-03-14 17:52:15 +08:00
|
|
|
close(dirfd);
|
2015-01-22 10:40:05 +08:00
|
|
|
if (local_err) {
|
|
|
|
g_free(buf);
|
|
|
|
error_propagate(errp, local_err);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
info = g_new0(GuestMemoryBlockInfo, 1);
|
|
|
|
info->size = strtol(buf, NULL, 16); /* the unit is bytes */
|
|
|
|
|
|
|
|
g_free(buf);
|
|
|
|
|
|
|
|
return info;
|
2015-01-22 10:40:02 +08:00
|
|
|
}
|
|
|
|
|
2022-05-20 10:19:35 +08:00
|
|
|
#define MAX_NAME_LEN 128
|
|
|
|
static GuestDiskStatsInfoList *guest_get_diskstats(Error **errp)
|
|
|
|
{
|
|
|
|
#ifdef CONFIG_LINUX
|
|
|
|
GuestDiskStatsInfoList *head = NULL, **tail = &head;
|
|
|
|
const char *diskstats = "/proc/diskstats";
|
|
|
|
FILE *fp;
|
|
|
|
size_t n;
|
|
|
|
char *line = NULL;
|
|
|
|
|
|
|
|
fp = fopen(diskstats, "r");
|
|
|
|
if (fp == NULL) {
|
|
|
|
error_setg_errno(errp, errno, "open(\"%s\")", diskstats);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (getline(&line, &n, fp) != -1) {
|
|
|
|
g_autofree GuestDiskStatsInfo *diskstatinfo = NULL;
|
|
|
|
g_autofree GuestDiskStats *diskstat = 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 = 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 = g_new0(GuestDiskStatsInfo, 1);
|
|
|
|
diskstatinfo->name = g_strdup(dev_name);
|
|
|
|
diskstatinfo->major = major;
|
|
|
|
diskstatinfo->minor = minor;
|
|
|
|
|
|
|
|
diskstat = g_new0(GuestDiskStats, 1);
|
|
|
|
if (i == 7) {
|
|
|
|
diskstat->has_read_ios = true;
|
|
|
|
diskstat->read_ios = rd_ios;
|
|
|
|
diskstat->has_read_sectors = true;
|
|
|
|
diskstat->read_sectors = rd_merges_or_rd_sec;
|
|
|
|
diskstat->has_write_ios = true;
|
|
|
|
diskstat->write_ios = rd_sec_or_wr_ios;
|
|
|
|
diskstat->has_write_sectors = true;
|
|
|
|
diskstat->write_sectors = rd_ticks_or_wr_sec;
|
|
|
|
}
|
|
|
|
if (i >= 14) {
|
|
|
|
diskstat->has_read_ios = true;
|
|
|
|
diskstat->read_ios = rd_ios;
|
|
|
|
diskstat->has_read_sectors = true;
|
|
|
|
diskstat->read_sectors = rd_sec_or_wr_ios;
|
|
|
|
diskstat->has_read_merges = true;
|
|
|
|
diskstat->read_merges = rd_merges_or_rd_sec;
|
|
|
|
diskstat->has_read_ticks = true;
|
|
|
|
diskstat->read_ticks = rd_ticks_or_wr_sec;
|
|
|
|
diskstat->has_write_ios = true;
|
|
|
|
diskstat->write_ios = wr_ios;
|
|
|
|
diskstat->has_write_sectors = true;
|
|
|
|
diskstat->write_sectors = wr_sec;
|
|
|
|
diskstat->has_write_merges = true;
|
|
|
|
diskstat->write_merges = wr_merges;
|
|
|
|
diskstat->has_write_ticks = true;
|
|
|
|
diskstat->write_ticks = wr_ticks;
|
|
|
|
diskstat->has_ios_pgr = true;
|
|
|
|
diskstat->ios_pgr = ios_pgr;
|
|
|
|
diskstat->has_total_ticks = true;
|
|
|
|
diskstat->total_ticks = tot_ticks;
|
|
|
|
diskstat->has_weight_ticks = true;
|
|
|
|
diskstat->weight_ticks = rq_ticks;
|
|
|
|
}
|
|
|
|
if (i >= 18) {
|
|
|
|
diskstat->has_discard_ios = true;
|
|
|
|
diskstat->discard_ios = dc_ios;
|
|
|
|
diskstat->has_discard_merges = true;
|
|
|
|
diskstat->discard_merges = dc_merges;
|
|
|
|
diskstat->has_discard_sectors = true;
|
|
|
|
diskstat->discard_sectors = dc_sec;
|
|
|
|
diskstat->has_discard_ticks = true;
|
|
|
|
diskstat->discard_ticks = dc_ticks;
|
|
|
|
}
|
|
|
|
if (i >= 20) {
|
|
|
|
diskstat->has_flush_ios = true;
|
|
|
|
diskstat->flush_ios = fl_ios;
|
|
|
|
diskstat->has_flush_ticks = true;
|
|
|
|
diskstat->flush_ticks = fl_ticks;
|
|
|
|
}
|
|
|
|
|
|
|
|
diskstatinfo->stats = g_steal_pointer(&diskstat);
|
|
|
|
QAPI_LIST_APPEND(tail, diskstatinfo);
|
|
|
|
diskstatinfo = 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);
|
|
|
|
}
|
|
|
|
|
2022-07-07 08:56:02 +08:00
|
|
|
GuestCpuStatsList *qmp_guest_get_cpustats(Error **errp)
|
|
|
|
{
|
|
|
|
GuestCpuStatsList *head = NULL, **tail = &head;
|
|
|
|
const char *cpustats = "/proc/stat";
|
|
|
|
int clk_tck = sysconf(_SC_CLK_TCK);
|
|
|
|
FILE *fp;
|
|
|
|
size_t n;
|
|
|
|
char *line = NULL;
|
|
|
|
|
|
|
|
fp = fopen(cpustats, "r");
|
|
|
|
if (fp == NULL) {
|
|
|
|
error_setg_errno(errp, errno, "open(\"%s\")", cpustats);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (getline(&line, &n, fp) != -1) {
|
|
|
|
GuestCpuStats *cpustat = NULL;
|
|
|
|
GuestLinuxCpuStats *linuxcpustat;
|
|
|
|
int i;
|
|
|
|
unsigned long user, system, idle, iowait, irq, softirq, steal, guest;
|
|
|
|
unsigned long nice, guest_nice;
|
|
|
|
char name[64];
|
|
|
|
|
|
|
|
i = sscanf(line, "%s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu",
|
|
|
|
name, &user, &nice, &system, &idle, &iowait, &irq, &softirq,
|
|
|
|
&steal, &guest, &guest_nice);
|
|
|
|
|
|
|
|
/* drop "cpu 1 2 3 ...", get "cpuX 1 2 3 ..." only */
|
|
|
|
if ((i == EOF) || strncmp(name, "cpu", 3) || (name[3] == '\0')) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i < 5) {
|
|
|
|
slog("Parsing cpu stat from %s failed, see \"man proc\"", cpustats);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
cpustat = g_new0(GuestCpuStats, 1);
|
|
|
|
cpustat->type = GUEST_CPU_STATS_TYPE_LINUX;
|
|
|
|
|
|
|
|
linuxcpustat = &cpustat->u.q_linux;
|
|
|
|
linuxcpustat->cpu = atoi(&name[3]);
|
|
|
|
linuxcpustat->user = user * 1000 / clk_tck;
|
|
|
|
linuxcpustat->nice = nice * 1000 / clk_tck;
|
|
|
|
linuxcpustat->system = system * 1000 / clk_tck;
|
|
|
|
linuxcpustat->idle = idle * 1000 / clk_tck;
|
|
|
|
|
|
|
|
if (i > 5) {
|
|
|
|
linuxcpustat->has_iowait = true;
|
|
|
|
linuxcpustat->iowait = iowait * 1000 / clk_tck;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i > 6) {
|
|
|
|
linuxcpustat->has_irq = true;
|
|
|
|
linuxcpustat->irq = irq * 1000 / clk_tck;
|
|
|
|
linuxcpustat->has_softirq = true;
|
|
|
|
linuxcpustat->softirq = softirq * 1000 / clk_tck;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i > 8) {
|
|
|
|
linuxcpustat->has_steal = true;
|
|
|
|
linuxcpustat->steal = steal * 1000 / clk_tck;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i > 9) {
|
|
|
|
linuxcpustat->has_guest = true;
|
|
|
|
linuxcpustat->guest = guest * 1000 / clk_tck;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i > 10) {
|
|
|
|
linuxcpustat->has_guest = true;
|
|
|
|
linuxcpustat->guest = guest * 1000 / clk_tck;
|
|
|
|
linuxcpustat->has_guestnice = true;
|
|
|
|
linuxcpustat->guestnice = guest_nice * 1000 / clk_tck;
|
|
|
|
}
|
|
|
|
|
|
|
|
QAPI_LIST_APPEND(tail, cpustat);
|
|
|
|
}
|
|
|
|
|
|
|
|
free(line);
|
|
|
|
fclose(fp);
|
|
|
|
return head;
|
|
|
|
}
|
|
|
|
|
2012-03-26 02:59:41 +08:00
|
|
|
#else /* defined(__linux__) */
|
|
|
|
|
2014-05-02 19:26:30 +08:00
|
|
|
void qmp_guest_suspend_disk(Error **errp)
|
2012-03-26 02:59:41 +08:00
|
|
|
{
|
2015-03-17 18:54:50 +08:00
|
|
|
error_setg(errp, QERR_UNSUPPORTED);
|
2012-03-26 02:59:41 +08:00
|
|
|
}
|
|
|
|
|
2014-05-02 19:26:30 +08:00
|
|
|
void qmp_guest_suspend_ram(Error **errp)
|
2012-03-26 02:59:41 +08:00
|
|
|
{
|
2015-03-17 18:54:50 +08:00
|
|
|
error_setg(errp, QERR_UNSUPPORTED);
|
2012-03-26 02:59:41 +08:00
|
|
|
}
|
|
|
|
|
2014-05-02 19:26:30 +08:00
|
|
|
void qmp_guest_suspend_hybrid(Error **errp)
|
2012-03-26 02:59:41 +08:00
|
|
|
{
|
2015-03-17 18:54:50 +08:00
|
|
|
error_setg(errp, QERR_UNSUPPORTED);
|
2012-03-26 02:59:41 +08:00
|
|
|
}
|
|
|
|
|
2013-03-07 05:59:30 +08:00
|
|
|
GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp)
|
|
|
|
{
|
2015-03-17 18:54:50 +08:00
|
|
|
error_setg(errp, QERR_UNSUPPORTED);
|
2013-03-07 05:59:30 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-03-07 05:59:31 +08:00
|
|
|
int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp)
|
|
|
|
{
|
2015-03-17 18:54:50 +08:00
|
|
|
error_setg(errp, QERR_UNSUPPORTED);
|
2013-03-07 05:59:31 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2015-01-22 10:40:02 +08:00
|
|
|
GuestMemoryBlockList *qmp_guest_get_memory_blocks(Error **errp)
|
|
|
|
{
|
2015-03-17 18:54:50 +08:00
|
|
|
error_setg(errp, QERR_UNSUPPORTED);
|
2015-01-22 10:40:02 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
GuestMemoryBlockResponseList *
|
|
|
|
qmp_guest_set_memory_blocks(GuestMemoryBlockList *mem_blks, Error **errp)
|
|
|
|
{
|
2015-03-17 18:54:50 +08:00
|
|
|
error_setg(errp, QERR_UNSUPPORTED);
|
2015-01-22 10:40:02 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
GuestMemoryBlockInfo *qmp_guest_get_memory_block_info(Error **errp)
|
|
|
|
{
|
2015-03-17 18:54:50 +08:00
|
|
|
error_setg(errp, QERR_UNSUPPORTED);
|
2015-01-22 10:40:02 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2012-04-14 10:07:36 +08:00
|
|
|
#endif
|
|
|
|
|
2022-04-27 03:55:22 +08:00
|
|
|
#ifdef HAVE_GETIFADDRS
|
|
|
|
static GuestNetworkInterface *
|
|
|
|
guest_find_interface(GuestNetworkInterfaceList *head,
|
|
|
|
const char *name)
|
|
|
|
{
|
|
|
|
for (; head; head = head->next) {
|
|
|
|
if (strcmp(head->value->name, name) == 0) {
|
|
|
|
return head->value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int guest_get_network_stats(const char *name,
|
|
|
|
GuestNetworkInterfaceStat *stats)
|
|
|
|
{
|
2022-04-27 03:55:24 +08:00
|
|
|
#ifdef CONFIG_LINUX
|
2022-04-27 03:55:22 +08:00
|
|
|
int name_len;
|
|
|
|
char const *devinfo = "/proc/net/dev";
|
|
|
|
FILE *fp;
|
|
|
|
char *line = NULL, *colon;
|
|
|
|
size_t n = 0;
|
|
|
|
fp = fopen(devinfo, "r");
|
|
|
|
if (!fp) {
|
2022-04-27 03:55:25 +08:00
|
|
|
g_debug("failed to open network stats %s: %s", devinfo,
|
|
|
|
g_strerror(errno));
|
2022-04-27 03:55:22 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
name_len = strlen(name);
|
|
|
|
while (getline(&line, &n, fp) != -1) {
|
|
|
|
long long dummy;
|
|
|
|
long long rx_bytes;
|
|
|
|
long long rx_packets;
|
|
|
|
long long rx_errs;
|
|
|
|
long long rx_dropped;
|
|
|
|
long long tx_bytes;
|
|
|
|
long long tx_packets;
|
|
|
|
long long tx_errs;
|
|
|
|
long long tx_dropped;
|
|
|
|
char *trim_line;
|
|
|
|
trim_line = g_strchug(line);
|
|
|
|
if (trim_line[0] == '\0') {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
colon = strchr(trim_line, ':');
|
|
|
|
if (!colon) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (colon - name_len == trim_line &&
|
|
|
|
strncmp(trim_line, name, name_len) == 0) {
|
|
|
|
if (sscanf(colon + 1,
|
|
|
|
"%lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld",
|
|
|
|
&rx_bytes, &rx_packets, &rx_errs, &rx_dropped,
|
|
|
|
&dummy, &dummy, &dummy, &dummy,
|
|
|
|
&tx_bytes, &tx_packets, &tx_errs, &tx_dropped,
|
|
|
|
&dummy, &dummy, &dummy, &dummy) != 16) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
stats->rx_bytes = rx_bytes;
|
|
|
|
stats->rx_packets = rx_packets;
|
|
|
|
stats->rx_errs = rx_errs;
|
|
|
|
stats->rx_dropped = rx_dropped;
|
|
|
|
stats->tx_bytes = tx_bytes;
|
|
|
|
stats->tx_packets = tx_packets;
|
|
|
|
stats->tx_errs = tx_errs;
|
|
|
|
stats->tx_dropped = tx_dropped;
|
|
|
|
fclose(fp);
|
|
|
|
g_free(line);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fclose(fp);
|
|
|
|
g_free(line);
|
|
|
|
g_debug("/proc/net/dev: Interface '%s' not found", name);
|
2022-04-27 03:55:25 +08:00
|
|
|
#else /* !CONFIG_LINUX */
|
|
|
|
g_debug("Network stats reporting available only for Linux");
|
|
|
|
#endif /* !CONFIG_LINUX */
|
2022-04-27 03:55:22 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2022-11-12 19:40:43 +08:00
|
|
|
#ifndef CONFIG_BSD
|
2022-10-17 15:28:24 +08:00
|
|
|
/*
|
|
|
|
* Fill "buf" with MAC address by ifaddrs. Pointer buf must point to a
|
|
|
|
* buffer with ETHER_ADDR_LEN length at least.
|
|
|
|
*
|
|
|
|
* Returns false in case of an error, otherwise true. "obtained" argument
|
|
|
|
* is true if a MAC address was obtained successful, otherwise false.
|
|
|
|
*/
|
|
|
|
bool guest_get_hw_addr(struct ifaddrs *ifa, unsigned char *buf,
|
|
|
|
bool *obtained, Error **errp)
|
|
|
|
{
|
|
|
|
struct ifreq ifr;
|
|
|
|
int sock;
|
|
|
|
|
|
|
|
*obtained = false;
|
|
|
|
|
|
|
|
/* we haven't obtained HW address yet */
|
|
|
|
sock = socket(PF_INET, SOCK_STREAM, 0);
|
|
|
|
if (sock == -1) {
|
|
|
|
error_setg_errno(errp, errno, "failed to create socket");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(&ifr, 0, sizeof(ifr));
|
|
|
|
pstrcpy(ifr.ifr_name, IF_NAMESIZE, ifa->ifa_name);
|
|
|
|
if (ioctl(sock, SIOCGIFHWADDR, &ifr) == -1) {
|
|
|
|
/*
|
|
|
|
* We can't get the hw addr of this interface, but that's not a
|
|
|
|
* fatal error.
|
|
|
|
*/
|
|
|
|
if (errno == EADDRNOTAVAIL) {
|
|
|
|
/* The interface doesn't have a hw addr (e.g. loopback). */
|
|
|
|
g_debug("failed to get MAC address of %s: %s",
|
|
|
|
ifa->ifa_name, strerror(errno));
|
|
|
|
} else{
|
|
|
|
g_warning("failed to get MAC address of %s: %s",
|
|
|
|
ifa->ifa_name, strerror(errno));
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
#ifdef CONFIG_SOLARIS
|
|
|
|
memcpy(buf, &ifr.ifr_addr.sa_data, ETHER_ADDR_LEN);
|
|
|
|
#else
|
|
|
|
memcpy(buf, &ifr.ifr_hwaddr.sa_data, ETHER_ADDR_LEN);
|
|
|
|
#endif
|
|
|
|
*obtained = true;
|
|
|
|
}
|
|
|
|
close(sock);
|
|
|
|
return true;
|
|
|
|
}
|
2022-11-12 19:40:43 +08:00
|
|
|
#endif /* CONFIG_BSD */
|
2022-10-17 15:28:24 +08:00
|
|
|
|
2022-04-27 03:55:22 +08:00
|
|
|
/*
|
|
|
|
* Build information about guest interfaces
|
|
|
|
*/
|
|
|
|
GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp)
|
|
|
|
{
|
|
|
|
GuestNetworkInterfaceList *head = NULL, **tail = &head;
|
|
|
|
struct ifaddrs *ifap, *ifa;
|
|
|
|
|
|
|
|
if (getifaddrs(&ifap) < 0) {
|
|
|
|
error_setg_errno(errp, errno, "getifaddrs failed");
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
|
|
|
|
GuestNetworkInterface *info;
|
|
|
|
GuestIpAddressList **address_tail;
|
|
|
|
GuestIpAddress *address_item = NULL;
|
|
|
|
GuestNetworkInterfaceStat *interface_stat = NULL;
|
|
|
|
char addr4[INET_ADDRSTRLEN];
|
|
|
|
char addr6[INET6_ADDRSTRLEN];
|
2022-10-17 15:28:24 +08:00
|
|
|
unsigned char mac_addr[ETHER_ADDR_LEN];
|
|
|
|
bool obtained;
|
2022-04-27 03:55:22 +08:00
|
|
|
void *p;
|
|
|
|
|
|
|
|
g_debug("Processing %s interface", ifa->ifa_name);
|
|
|
|
|
|
|
|
info = guest_find_interface(head, ifa->ifa_name);
|
|
|
|
|
|
|
|
if (!info) {
|
|
|
|
info = g_malloc0(sizeof(*info));
|
|
|
|
info->name = g_strdup(ifa->ifa_name);
|
|
|
|
|
|
|
|
QAPI_LIST_APPEND(tail, info);
|
|
|
|
}
|
|
|
|
|
2022-11-05 00:07:11 +08:00
|
|
|
if (!info->hardware_address) {
|
2022-10-17 15:28:24 +08:00
|
|
|
if (!guest_get_hw_addr(ifa, mac_addr, &obtained, errp)) {
|
2022-04-27 03:55:22 +08:00
|
|
|
goto error;
|
|
|
|
}
|
2022-10-17 15:28:24 +08:00
|
|
|
if (obtained) {
|
qga/commands-posix: Fix iface hw address detection
Since its introduction in commit 3424fc9f16a1 ("qemu-ga: add
guest-network-get-interfaces command"), guest-network-get-interfaces
seems to check if a given interface has a hardware address by checking
'ifa->ifa_flags & SIOCGIFHWADDR'. But ifa_flags is a field for IFF_*
flags (IFF_UP, IFF_LOOPBACK, etc), and comparing it to an ioctl like
SIOCGIFHWADDR doesn't make sense.
On Linux, this isn't a big deal, since SIOCGIFHWADDR has so many bits
set (0x8927), 'ifa->ifa_flags & SIOCGIFHWADDR' will usually have a
nonzero result for any 'normal'-looking interfaces: anything with
IFF_UP (0x1) or IFF_BROADCAST (0x2) set, as well as several
less-common flags. This means we'll try to get the hardware address
for most/all interfaces, even those that don't really have one (like
the loopback device). For those interfaces, Linux just returns a
hardware address of all zeroes.
On Solaris, however, trying to get the hardware address for a loopback
device returns an EADDRNOTAVAIL error. This causes us to return an
error and the entire guest-network-get-interfaces call fails.
Change this logic to always try to get the hardware address for each
interface, and don't return an error if we fail to get it. Instead,
just don't include the 'hardware-address' field in the result if we
can't get the hardware address.
Signed-off-by: Andrew Deason <adeason@sinenomine.net>
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
Message-Id: <20220426195526.7699-3-adeason@sinenomine.net>
2022-04-27 03:55:23 +08:00
|
|
|
info->hardware_address =
|
|
|
|
g_strdup_printf("%02x:%02x:%02x:%02x:%02x:%02x",
|
|
|
|
(int) mac_addr[0], (int) mac_addr[1],
|
|
|
|
(int) mac_addr[2], (int) mac_addr[3],
|
|
|
|
(int) mac_addr[4], (int) mac_addr[5]);
|
|
|
|
}
|
2022-04-27 03:55:22 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (ifa->ifa_addr &&
|
|
|
|
ifa->ifa_addr->sa_family == AF_INET) {
|
|
|
|
/* interface with IPv4 address */
|
|
|
|
p = &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
|
|
|
|
if (!inet_ntop(AF_INET, p, addr4, sizeof(addr4))) {
|
|
|
|
error_setg_errno(errp, errno, "inet_ntop failed");
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
address_item = g_malloc0(sizeof(*address_item));
|
|
|
|
address_item->ip_address = g_strdup(addr4);
|
|
|
|
address_item->ip_address_type = GUEST_IP_ADDRESS_TYPE_IPV4;
|
|
|
|
|
|
|
|
if (ifa->ifa_netmask) {
|
|
|
|
/* Count the number of set bits in netmask.
|
|
|
|
* This is safe as '1' and '0' cannot be shuffled in netmask. */
|
|
|
|
p = &((struct sockaddr_in *)ifa->ifa_netmask)->sin_addr;
|
|
|
|
address_item->prefix = ctpop32(((uint32_t *) p)[0]);
|
|
|
|
}
|
|
|
|
} else if (ifa->ifa_addr &&
|
|
|
|
ifa->ifa_addr->sa_family == AF_INET6) {
|
|
|
|
/* interface with IPv6 address */
|
|
|
|
p = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
|
|
|
|
if (!inet_ntop(AF_INET6, p, addr6, sizeof(addr6))) {
|
|
|
|
error_setg_errno(errp, errno, "inet_ntop failed");
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
address_item = g_malloc0(sizeof(*address_item));
|
|
|
|
address_item->ip_address = g_strdup(addr6);
|
|
|
|
address_item->ip_address_type = GUEST_IP_ADDRESS_TYPE_IPV6;
|
|
|
|
|
|
|
|
if (ifa->ifa_netmask) {
|
|
|
|
/* Count the number of set bits in netmask.
|
|
|
|
* This is safe as '1' and '0' cannot be shuffled in netmask. */
|
|
|
|
p = &((struct sockaddr_in6 *)ifa->ifa_netmask)->sin6_addr;
|
|
|
|
address_item->prefix =
|
|
|
|
ctpop32(((uint32_t *) p)[0]) +
|
|
|
|
ctpop32(((uint32_t *) p)[1]) +
|
|
|
|
ctpop32(((uint32_t *) p)[2]) +
|
|
|
|
ctpop32(((uint32_t *) p)[3]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!address_item) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
address_tail = &info->ip_addresses;
|
|
|
|
while (*address_tail) {
|
|
|
|
address_tail = &(*address_tail)->next;
|
|
|
|
}
|
|
|
|
QAPI_LIST_APPEND(address_tail, address_item);
|
|
|
|
|
|
|
|
info->has_ip_addresses = true;
|
|
|
|
|
2022-11-05 00:07:11 +08:00
|
|
|
if (!info->statistics) {
|
2022-04-27 03:55:22 +08:00
|
|
|
interface_stat = g_malloc0(sizeof(*interface_stat));
|
|
|
|
if (guest_get_network_stats(info->name, interface_stat) == -1) {
|
|
|
|
g_free(interface_stat);
|
|
|
|
} else {
|
|
|
|
info->statistics = interface_stat;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
freeifaddrs(ifap);
|
|
|
|
return head;
|
|
|
|
|
|
|
|
error:
|
|
|
|
freeifaddrs(ifap);
|
|
|
|
qapi_free_GuestNetworkInterfaceList(head);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp)
|
|
|
|
{
|
|
|
|
error_setg(errp, QERR_UNSUPPORTED);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* HAVE_GETIFADDRS */
|
|
|
|
|
2012-04-14 10:07:36 +08:00
|
|
|
#if !defined(CONFIG_FSFREEZE)
|
|
|
|
|
qga: Add guest-get-fsinfo command
Add command to get mounted filesystems information in the guest.
The returned value contains a list of mountpoint paths and
corresponding disks info such as disk bus type, drive address,
and the disk controllers' PCI addresses, so that management layer
such as libvirt can resolve the disk backends.
For example, when `lsblk' result is:
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sdb 8:16 0 1G 0 disk
`-sdb1 8:17 0 1024M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
sdc 8:32 0 1G 0 disk
`-sdc1 8:33 0 512M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
vda 252:0 0 25G 0 disk
`-vda1 252:1 0 25G 0 part /
where sdb is a SCSI disk with PCI controller 0000:00:0a.0 and ID=1,
sdc is an IDE disk with PCI controller 0000:00:01.1, and
vda is a virtio-blk disk with PCI device 0000:00:06.0,
guest-get-fsinfo command will return the following result:
{"return":
[{"name":"dm-1",
"mountpoint":"/mnt/test",
"disk":[
{"bus-type":"scsi","bus":0,"unit":1,"target":0,
"pci-controller":{"bus":0,"slot":10,"domain":0,"function":0}},
{"bus-type":"ide","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":1,"domain":0,"function":1}}],
"type":"xfs"},
{"name":"vda1", "mountpoint":"/",
"disk":[
{"bus-type":"virtio","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":6,"domain":0,"function":0}}],
"type":"ext4"}]}
In Linux guest, the disk information is resolved from sysfs. So far,
it only supports virtio-blk, virtio-scsi, IDE, SATA, SCSI disks on x86
hosts, and "disk" parameter may be empty for unsupported disk types.
Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama@hds.com>
*updated schema to report 2.2 as initial supported version
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2014-07-01 05:51:34 +08:00
|
|
|
GuestFilesystemInfoList *qmp_guest_get_fsinfo(Error **errp)
|
|
|
|
{
|
2015-03-17 18:54:50 +08:00
|
|
|
error_setg(errp, QERR_UNSUPPORTED);
|
qga: Add guest-get-fsinfo command
Add command to get mounted filesystems information in the guest.
The returned value contains a list of mountpoint paths and
corresponding disks info such as disk bus type, drive address,
and the disk controllers' PCI addresses, so that management layer
such as libvirt can resolve the disk backends.
For example, when `lsblk' result is:
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sdb 8:16 0 1G 0 disk
`-sdb1 8:17 0 1024M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
sdc 8:32 0 1G 0 disk
`-sdc1 8:33 0 512M 0 part
`-vg0-lv0 253:1 0 1.4G 0 lvm /mnt/test
vda 252:0 0 25G 0 disk
`-vda1 252:1 0 25G 0 part /
where sdb is a SCSI disk with PCI controller 0000:00:0a.0 and ID=1,
sdc is an IDE disk with PCI controller 0000:00:01.1, and
vda is a virtio-blk disk with PCI device 0000:00:06.0,
guest-get-fsinfo command will return the following result:
{"return":
[{"name":"dm-1",
"mountpoint":"/mnt/test",
"disk":[
{"bus-type":"scsi","bus":0,"unit":1,"target":0,
"pci-controller":{"bus":0,"slot":10,"domain":0,"function":0}},
{"bus-type":"ide","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":1,"domain":0,"function":1}}],
"type":"xfs"},
{"name":"vda1", "mountpoint":"/",
"disk":[
{"bus-type":"virtio","bus":0,"unit":0,"target":0,
"pci-controller":{"bus":0,"slot":6,"domain":0,"function":0}}],
"type":"ext4"}]}
In Linux guest, the disk information is resolved from sysfs. So far,
it only supports virtio-blk, virtio-scsi, IDE, SATA, SCSI disks on x86
hosts, and "disk" parameter may be empty for unsupported disk types.
Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama@hds.com>
*updated schema to report 2.2 as initial supported version
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2014-07-01 05:51:34 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2014-05-02 19:26:30 +08:00
|
|
|
GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **errp)
|
2012-03-26 02:59:41 +08:00
|
|
|
{
|
2015-03-17 18:54:50 +08:00
|
|
|
error_setg(errp, QERR_UNSUPPORTED);
|
2012-04-14 10:07:36 +08:00
|
|
|
|
|
|
|
return 0;
|
2012-03-26 02:59:41 +08:00
|
|
|
}
|
|
|
|
|
2014-05-02 19:26:30 +08:00
|
|
|
int64_t qmp_guest_fsfreeze_freeze(Error **errp)
|
2012-03-26 02:59:41 +08:00
|
|
|
{
|
2015-03-17 18:54:50 +08:00
|
|
|
error_setg(errp, QERR_UNSUPPORTED);
|
2012-04-14 10:07:36 +08:00
|
|
|
|
|
|
|
return 0;
|
2012-03-26 02:59:41 +08:00
|
|
|
}
|
|
|
|
|
2014-07-01 05:51:27 +08:00
|
|
|
int64_t qmp_guest_fsfreeze_freeze_list(bool has_mountpoints,
|
|
|
|
strList *mountpoints,
|
|
|
|
Error **errp)
|
|
|
|
{
|
2015-03-17 18:54:50 +08:00
|
|
|
error_setg(errp, QERR_UNSUPPORTED);
|
2014-07-01 05:51:27 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-05-02 19:26:30 +08:00
|
|
|
int64_t qmp_guest_fsfreeze_thaw(Error **errp)
|
2012-03-26 02:59:41 +08:00
|
|
|
{
|
2015-03-17 18:54:50 +08:00
|
|
|
error_setg(errp, QERR_UNSUPPORTED);
|
2012-04-14 10:07:36 +08:00
|
|
|
|
|
|
|
return 0;
|
2012-03-26 02:59:41 +08:00
|
|
|
}
|
qga: add implementation of guest-get-disks for Linux
The command lists all disks (real and virtual) as well as disk
partitions. For each disk the list of dependent disks is also listed and
/dev path is used as a handle so it can be matched with "name" field of
other returned disk entries. For disk partitions the "dependents" list
is populated with the the parent device for easier tracking of
hierarchy.
Example output:
{
"return": [
...
{
"name": "/dev/dm-0",
"partition": false,
"dependents": [
"/dev/sda2"
],
"alias": "luks-7062202e-5b9b-433e-81e8-6628c40da9f7"
},
{
"name": "/dev/sda2",
"partition": true,
"dependents": [
"/dev/sda"
]
},
{
"name": "/dev/sda",
"partition": false,
"address": {
"serial": "SAMSUNG_MZ7LN512HCHP-000L1_S1ZKNXAG822493",
"bus-type": "sata",
...
"dev": "/dev/sda",
"target": 0
},
"dependents": []
},
...
]
}
Signed-off-by: Tomáš Golembiovský <tgolembi@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
*add missing stub for !defined(CONFIG_FSFREEZE)
*remove unused deps_dir variable
Signed-off-by: Michael Roth <michael.roth@amd.com>
2020-10-12 16:36:02 +08:00
|
|
|
|
|
|
|
GuestDiskInfoList *qmp_guest_get_disks(Error **errp)
|
|
|
|
{
|
|
|
|
error_setg(errp, QERR_UNSUPPORTED);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2022-05-20 10:19:35 +08:00
|
|
|
GuestDiskStatsInfoList *qmp_guest_get_diskstats(Error **errp)
|
|
|
|
{
|
|
|
|
error_setg(errp, QERR_UNSUPPORTED);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2022-07-07 08:56:02 +08:00
|
|
|
GuestCpuStatsList *qmp_guest_get_cpustats(Error **errp)
|
|
|
|
{
|
|
|
|
error_setg(errp, QERR_UNSUPPORTED);
|
|
|
|
return NULL;
|
|
|
|
}
|
2022-05-20 10:19:35 +08:00
|
|
|
|
2012-06-13 13:41:28 +08:00
|
|
|
#endif /* CONFIG_FSFREEZE */
|
|
|
|
|
|
|
|
#if !defined(CONFIG_FSTRIM)
|
2015-05-11 14:58:45 +08:00
|
|
|
GuestFilesystemTrimResponse *
|
|
|
|
qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp)
|
2012-06-13 13:41:28 +08:00
|
|
|
{
|
2015-03-17 18:54:50 +08:00
|
|
|
error_setg(errp, QERR_UNSUPPORTED);
|
2015-05-11 14:58:45 +08:00
|
|
|
return NULL;
|
2012-06-13 13:41:28 +08:00
|
|
|
}
|
2012-03-26 02:59:41 +08:00
|
|
|
#endif
|
|
|
|
|
2022-07-27 17:21:34 +08:00
|
|
|
/* add unsupported commands to the list of blocked RPCs */
|
|
|
|
GList *ga_command_init_blockedrpcs(GList *blockedrpcs)
|
2014-07-01 05:51:40 +08:00
|
|
|
{
|
|
|
|
#if !defined(__linux__)
|
|
|
|
{
|
|
|
|
const char *list[] = {
|
|
|
|
"guest-suspend-disk", "guest-suspend-ram",
|
2022-04-27 03:55:22 +08:00
|
|
|
"guest-suspend-hybrid", "guest-get-vcpus", "guest-set-vcpus",
|
2015-01-22 10:40:06 +08:00
|
|
|
"guest-get-memory-blocks", "guest-set-memory-blocks",
|
2019-10-17 20:34:38 +08:00
|
|
|
"guest-get-memory-block-size", "guest-get-memory-block-info",
|
|
|
|
NULL};
|
2014-07-01 05:51:40 +08:00
|
|
|
char **p = (char **)list;
|
|
|
|
|
|
|
|
while (*p) {
|
2022-07-27 17:21:34 +08:00
|
|
|
blockedrpcs = g_list_append(blockedrpcs, g_strdup(*p++));
|
2014-07-01 05:51:40 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2022-04-27 03:55:22 +08:00
|
|
|
#if !defined(HAVE_GETIFADDRS)
|
2022-07-27 17:21:34 +08:00
|
|
|
blockedrpcs = g_list_append(blockedrpcs,
|
2022-04-27 03:55:22 +08:00
|
|
|
g_strdup("guest-network-get-interfaces"));
|
|
|
|
#endif
|
|
|
|
|
2014-07-01 05:51:40 +08:00
|
|
|
#if !defined(CONFIG_FSFREEZE)
|
|
|
|
{
|
|
|
|
const char *list[] = {
|
|
|
|
"guest-get-fsinfo", "guest-fsfreeze-status",
|
|
|
|
"guest-fsfreeze-freeze", "guest-fsfreeze-freeze-list",
|
qga: add implementation of guest-get-disks for Linux
The command lists all disks (real and virtual) as well as disk
partitions. For each disk the list of dependent disks is also listed and
/dev path is used as a handle so it can be matched with "name" field of
other returned disk entries. For disk partitions the "dependents" list
is populated with the the parent device for easier tracking of
hierarchy.
Example output:
{
"return": [
...
{
"name": "/dev/dm-0",
"partition": false,
"dependents": [
"/dev/sda2"
],
"alias": "luks-7062202e-5b9b-433e-81e8-6628c40da9f7"
},
{
"name": "/dev/sda2",
"partition": true,
"dependents": [
"/dev/sda"
]
},
{
"name": "/dev/sda",
"partition": false,
"address": {
"serial": "SAMSUNG_MZ7LN512HCHP-000L1_S1ZKNXAG822493",
"bus-type": "sata",
...
"dev": "/dev/sda",
"target": 0
},
"dependents": []
},
...
]
}
Signed-off-by: Tomáš Golembiovský <tgolembi@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
*add missing stub for !defined(CONFIG_FSFREEZE)
*remove unused deps_dir variable
Signed-off-by: Michael Roth <michael.roth@amd.com>
2020-10-12 16:36:02 +08:00
|
|
|
"guest-fsfreeze-thaw", "guest-get-fsinfo",
|
|
|
|
"guest-get-disks", NULL};
|
2014-07-01 05:51:40 +08:00
|
|
|
char **p = (char **)list;
|
|
|
|
|
|
|
|
while (*p) {
|
2022-07-27 17:21:34 +08:00
|
|
|
blockedrpcs = g_list_append(blockedrpcs, g_strdup(*p++));
|
2014-07-01 05:51:40 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if !defined(CONFIG_FSTRIM)
|
2022-07-27 17:21:34 +08:00
|
|
|
blockedrpcs = g_list_append(blockedrpcs, g_strdup("guest-fstrim"));
|
2014-07-01 05:51:40 +08:00
|
|
|
#endif
|
|
|
|
|
2022-07-27 17:21:34 +08:00
|
|
|
blockedrpcs = g_list_append(blockedrpcs, g_strdup("guest-get-devices"));
|
2020-07-21 23:40:41 +08:00
|
|
|
|
2022-07-27 17:21:34 +08:00
|
|
|
return blockedrpcs;
|
2014-07-01 05:51:40 +08:00
|
|
|
}
|
|
|
|
|
2011-07-20 04:41:55 +08:00
|
|
|
/* register init/cleanup routines for stateful command groups */
|
|
|
|
void ga_command_state_init(GAState *s, GACommandState *cs)
|
|
|
|
{
|
2011-07-23 03:14:17 +08:00
|
|
|
#if defined(CONFIG_FSFREEZE)
|
2012-04-18 08:01:45 +08:00
|
|
|
ga_command_state_add(cs, NULL, guest_fsfreeze_cleanup);
|
2011-07-23 03:14:17 +08:00
|
|
|
#endif
|
2011-07-20 04:41:55 +08:00
|
|
|
}
|
2017-04-19 17:26:15 +08:00
|
|
|
|
2017-07-17 21:58:33 +08:00
|
|
|
#ifdef HAVE_UTMPX
|
|
|
|
|
2017-04-19 17:26:15 +08:00
|
|
|
#define QGA_MICRO_SECOND_TO_SECOND 1000000
|
|
|
|
|
|
|
|
static double ga_get_login_time(struct utmpx *user_info)
|
|
|
|
{
|
|
|
|
double seconds = (double)user_info->ut_tv.tv_sec;
|
|
|
|
double useconds = (double)user_info->ut_tv.tv_usec;
|
|
|
|
useconds /= QGA_MICRO_SECOND_TO_SECOND;
|
|
|
|
return seconds + useconds;
|
|
|
|
}
|
|
|
|
|
2019-12-06 01:46:26 +08:00
|
|
|
GuestUserList *qmp_guest_get_users(Error **errp)
|
2017-04-19 17:26:15 +08:00
|
|
|
{
|
|
|
|
GHashTable *cache = NULL;
|
2021-01-14 06:10:13 +08:00
|
|
|
GuestUserList *head = NULL, **tail = &head;
|
2017-04-19 17:26:15 +08:00
|
|
|
struct utmpx *user_info = NULL;
|
|
|
|
gpointer value = NULL;
|
|
|
|
GuestUser *user = NULL;
|
|
|
|
double login_time = 0;
|
|
|
|
|
|
|
|
cache = g_hash_table_new(g_str_hash, g_str_equal);
|
|
|
|
setutxent();
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
user_info = getutxent();
|
|
|
|
if (user_info == NULL) {
|
|
|
|
break;
|
|
|
|
} else if (user_info->ut_type != USER_PROCESS) {
|
|
|
|
continue;
|
|
|
|
} else if (g_hash_table_contains(cache, user_info->ut_user)) {
|
|
|
|
value = g_hash_table_lookup(cache, user_info->ut_user);
|
|
|
|
user = (GuestUser *)value;
|
|
|
|
login_time = ga_get_login_time(user_info);
|
|
|
|
/* We're ensuring the earliest login time to be sent */
|
|
|
|
if (login_time < user->login_time) {
|
|
|
|
user->login_time = login_time;
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2021-01-14 06:10:13 +08:00
|
|
|
user = g_new0(GuestUser, 1);
|
|
|
|
user->user = g_strdup(user_info->ut_user);
|
|
|
|
user->login_time = ga_get_login_time(user_info);
|
2017-04-19 17:26:15 +08:00
|
|
|
|
2021-01-14 06:10:13 +08:00
|
|
|
g_hash_table_insert(cache, user->user, user);
|
2017-04-19 17:26:15 +08:00
|
|
|
|
2021-01-14 06:10:13 +08:00
|
|
|
QAPI_LIST_APPEND(tail, user);
|
2017-04-19 17:26:15 +08:00
|
|
|
}
|
|
|
|
endutxent();
|
|
|
|
g_hash_table_destroy(cache);
|
|
|
|
return head;
|
|
|
|
}
|
2017-07-17 21:58:33 +08:00
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
GuestUserList *qmp_guest_get_users(Error **errp)
|
|
|
|
{
|
|
|
|
error_setg(errp, QERR_UNSUPPORTED);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
2017-07-14 22:28:56 +08:00
|
|
|
|
|
|
|
/* Replace escaped special characters with theire real values. The replacement
|
|
|
|
* is done in place -- returned value is in the original string.
|
|
|
|
*/
|
|
|
|
static void ga_osrelease_replace_special(gchar *value)
|
|
|
|
{
|
|
|
|
gchar *p, *p2, quote;
|
|
|
|
|
|
|
|
/* Trim the string at first space or semicolon if it is not enclosed in
|
|
|
|
* single or double quotes. */
|
|
|
|
if ((value[0] != '"') || (value[0] == '\'')) {
|
|
|
|
p = strchr(value, ' ');
|
|
|
|
if (p != NULL) {
|
|
|
|
*p = 0;
|
|
|
|
}
|
|
|
|
p = strchr(value, ';');
|
|
|
|
if (p != NULL) {
|
|
|
|
*p = 0;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
quote = value[0];
|
|
|
|
p2 = value;
|
|
|
|
p = value + 1;
|
|
|
|
while (*p != 0) {
|
|
|
|
if (*p == '\\') {
|
|
|
|
p++;
|
|
|
|
switch (*p) {
|
|
|
|
case '$':
|
|
|
|
case '\'':
|
|
|
|
case '"':
|
|
|
|
case '\\':
|
|
|
|
case '`':
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* Keep literal backslash followed by whatever is there */
|
|
|
|
p--;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else if (*p == quote) {
|
|
|
|
*p2 = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
*(p2++) = *(p++);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static GKeyFile *ga_parse_osrelease(const char *fname)
|
|
|
|
{
|
|
|
|
gchar *content = NULL;
|
|
|
|
gchar *content2 = NULL;
|
|
|
|
GError *err = NULL;
|
|
|
|
GKeyFile *keys = g_key_file_new();
|
|
|
|
const char *group = "[os-release]\n";
|
|
|
|
|
|
|
|
if (!g_file_get_contents(fname, &content, NULL, &err)) {
|
|
|
|
slog("failed to read '%s', error: %s", fname, err->message);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!g_utf8_validate(content, -1, NULL)) {
|
|
|
|
slog("file is not utf-8 encoded: %s", fname);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
content2 = g_strdup_printf("%s%s", group, content);
|
|
|
|
|
|
|
|
if (!g_key_file_load_from_data(keys, content2, -1, G_KEY_FILE_NONE,
|
|
|
|
&err)) {
|
|
|
|
slog("failed to parse file '%s', error: %s", fname, err->message);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
g_free(content);
|
|
|
|
g_free(content2);
|
|
|
|
return keys;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
g_error_free(err);
|
|
|
|
g_free(content);
|
|
|
|
g_free(content2);
|
|
|
|
g_key_file_free(keys);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
GuestOSInfo *qmp_guest_get_osinfo(Error **errp)
|
|
|
|
{
|
|
|
|
GuestOSInfo *info = NULL;
|
|
|
|
struct utsname kinfo;
|
2017-07-14 22:28:58 +08:00
|
|
|
GKeyFile *osrelease = NULL;
|
|
|
|
const char *qga_os_release = g_getenv("QGA_OS_RELEASE");
|
2017-07-14 22:28:56 +08:00
|
|
|
|
|
|
|
info = g_new0(GuestOSInfo, 1);
|
|
|
|
|
|
|
|
if (uname(&kinfo) != 0) {
|
|
|
|
error_setg_errno(errp, errno, "uname failed");
|
|
|
|
} else {
|
|
|
|
info->kernel_version = g_strdup(kinfo.version);
|
|
|
|
info->kernel_release = g_strdup(kinfo.release);
|
|
|
|
info->machine = g_strdup(kinfo.machine);
|
|
|
|
}
|
|
|
|
|
2017-07-14 22:28:58 +08:00
|
|
|
if (qga_os_release != NULL) {
|
|
|
|
osrelease = ga_parse_osrelease(qga_os_release);
|
|
|
|
} else {
|
|
|
|
osrelease = ga_parse_osrelease("/etc/os-release");
|
|
|
|
if (osrelease == NULL) {
|
|
|
|
osrelease = ga_parse_osrelease("/usr/lib/os-release");
|
|
|
|
}
|
2017-07-14 22:28:56 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (osrelease != NULL) {
|
|
|
|
char *value;
|
|
|
|
|
|
|
|
#define GET_FIELD(field, osfield) do { \
|
|
|
|
value = g_key_file_get_value(osrelease, "os-release", osfield, NULL); \
|
|
|
|
if (value != NULL) { \
|
|
|
|
ga_osrelease_replace_special(value); \
|
|
|
|
info->field = value; \
|
|
|
|
} \
|
|
|
|
} while (0)
|
|
|
|
GET_FIELD(id, "ID");
|
|
|
|
GET_FIELD(name, "NAME");
|
|
|
|
GET_FIELD(pretty_name, "PRETTY_NAME");
|
|
|
|
GET_FIELD(version, "VERSION");
|
|
|
|
GET_FIELD(version_id, "VERSION_ID");
|
|
|
|
GET_FIELD(variant, "VARIANT");
|
|
|
|
GET_FIELD(variant_id, "VARIANT_ID");
|
|
|
|
#undef GET_FIELD
|
|
|
|
|
|
|
|
g_key_file_free(osrelease);
|
|
|
|
}
|
|
|
|
|
|
|
|
return info;
|
|
|
|
}
|
2020-07-21 23:40:41 +08:00
|
|
|
|
|
|
|
GuestDeviceInfoList *qmp_guest_get_devices(Error **errp)
|
|
|
|
{
|
|
|
|
error_setg(errp, QERR_UNSUPPORTED);
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
2022-04-20 21:26:14 +08:00
|
|
|
|
|
|
|
#ifndef HOST_NAME_MAX
|
|
|
|
# ifdef _POSIX_HOST_NAME_MAX
|
|
|
|
# define HOST_NAME_MAX _POSIX_HOST_NAME_MAX
|
|
|
|
# else
|
|
|
|
# define HOST_NAME_MAX 255
|
|
|
|
# endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
char *qga_get_host_name(Error **errp)
|
|
|
|
{
|
|
|
|
long len = -1;
|
|
|
|
g_autofree char *hostname = NULL;
|
|
|
|
|
|
|
|
#ifdef _SC_HOST_NAME_MAX
|
|
|
|
len = sysconf(_SC_HOST_NAME_MAX);
|
|
|
|
#endif /* _SC_HOST_NAME_MAX */
|
|
|
|
|
|
|
|
if (len < 0) {
|
|
|
|
len = HOST_NAME_MAX;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Unfortunately, gethostname() below does not guarantee a
|
|
|
|
* NULL terminated string. Therefore, allocate one byte more
|
|
|
|
* to be sure. */
|
|
|
|
hostname = g_new0(char, len + 1);
|
|
|
|
|
|
|
|
if (gethostname(hostname, len) < 0) {
|
|
|
|
error_setg_errno(errp, errno,
|
|
|
|
"cannot get hostname");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return g_steal_pointer(&hostname);
|
|
|
|
}
|