QGA Pull request
-----BEGIN PGP SIGNATURE----- iQJQBAABCAA6FiEEh6m9kz+HxgbSdvYt2ujhCXWWnOUFAmJyToAcHG1hcmNhbmRy ZS5sdXJlYXVAcmVkaGF0LmNvbQAKCRDa6OEJdZac5e0tD/4y6+wWkn+ZjdNknpwB LzbCtpohTvdRVJ9f22i/niFhKfAS25aB7CeQIuDrsC0Bc7+4NV49DeGPh4pnH05B fNYJH+6/xYuc0cYML8FSqntbe1ElaIUP9vUPjIv3S4+IPMSkAyNI0xWkCiPsW8H4 rHpMMmtBJQNiS1KxE0ahj1WOkwnNsDWxL80fh5RBwb3g2ZHrIjilOrs6MnCDRM8r vYHK8cfGg+Lp/4hXB0mqTHw6oEmtCSiOZqE2rnIiHEI2kbW0DFu7yxtEX6aBjM6a s6NkzYSkBzUjV3HOU/aWCTJOz6jL+nzsdAe413ll0VWv8UUVAgDnOfG7BewIW7jn LFpIjsTen7VqVp6n96k8lPkoInnszyKVrJ/4AnS3PDrdfgg7B0RIx/exjrd249YX XsZjcW/vF4gRuu0YyNZESgeR0/y/7HRN6X9dCtF5vsjeqswEWoqH0vSkca8fbZCR yjE9z/dUuWwCBwq03UmtAy6POPhEw8QMld7SPGFl5n6Z7569hhWErYnu6ey/Xwwb W6W2EdFRCWF3cR4nxDFCoJVFjFybYIl2qNLu01tJ1kqsaIJMxfyt1fP09Jd4tifk CrAS1zpZSgDYRbzA5UGZ8Xir10uy+GyjFxIYnDYpqRy6nTDuG7wP2Ua+w182/2fd RwbzF98vV/AMoEqSAiHVQ56KMw== =Fm6Z -----END PGP SIGNATURE----- Merge tag 'qga-pull-request' of gitlab.com:marcandre.lureau/qemu into staging QGA Pull request # -----BEGIN PGP SIGNATURE----- # # iQJQBAABCAA6FiEEh6m9kz+HxgbSdvYt2ujhCXWWnOUFAmJyToAcHG1hcmNhbmRy # ZS5sdXJlYXVAcmVkaGF0LmNvbQAKCRDa6OEJdZac5e0tD/4y6+wWkn+ZjdNknpwB # LzbCtpohTvdRVJ9f22i/niFhKfAS25aB7CeQIuDrsC0Bc7+4NV49DeGPh4pnH05B # fNYJH+6/xYuc0cYML8FSqntbe1ElaIUP9vUPjIv3S4+IPMSkAyNI0xWkCiPsW8H4 # rHpMMmtBJQNiS1KxE0ahj1WOkwnNsDWxL80fh5RBwb3g2ZHrIjilOrs6MnCDRM8r # vYHK8cfGg+Lp/4hXB0mqTHw6oEmtCSiOZqE2rnIiHEI2kbW0DFu7yxtEX6aBjM6a # s6NkzYSkBzUjV3HOU/aWCTJOz6jL+nzsdAe413ll0VWv8UUVAgDnOfG7BewIW7jn # LFpIjsTen7VqVp6n96k8lPkoInnszyKVrJ/4AnS3PDrdfgg7B0RIx/exjrd249YX # XsZjcW/vF4gRuu0YyNZESgeR0/y/7HRN6X9dCtF5vsjeqswEWoqH0vSkca8fbZCR # yjE9z/dUuWwCBwq03UmtAy6POPhEw8QMld7SPGFl5n6Z7569hhWErYnu6ey/Xwwb # W6W2EdFRCWF3cR4nxDFCoJVFjFybYIl2qNLu01tJ1kqsaIJMxfyt1fP09Jd4tifk # CrAS1zpZSgDYRbzA5UGZ8Xir10uy+GyjFxIYnDYpqRy6nTDuG7wP2Ua+w182/2fd # RwbzF98vV/AMoEqSAiHVQ56KMw== # =Fm6Z # -----END PGP SIGNATURE----- # gpg: Signature made Wed 04 May 2022 02:59:28 AM PDT # gpg: using RSA key 87A9BD933F87C606D276F62DDAE8E10975969CE5 # gpg: issuer "marcandre.lureau@redhat.com" # gpg: Good signature from "Marc-André Lureau <marcandre.lureau@redhat.com>" [full] # gpg: aka "Marc-André Lureau <marcandre.lureau@gmail.com>" [full] * tag 'qga-pull-request' of gitlab.com:marcandre.lureau/qemu: qga: Introduce disk smart qga: Introduce NVMe disk bus type qga/commands-posix: 'guest-shutdown' for Solaris qga/commands-posix: Log all net stats failures qga/commands-posix: Fix listing ifaces for Solaris qga/commands-posix: Fix iface hw address detection qga/commands-posix: Use getifaddrs when available Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
commit
9cf289af47
|
@ -1687,6 +1687,7 @@ config_host_data.set('CONFIG_SYNCFS', cc.has_function('syncfs'))
|
|||
config_host_data.set('CONFIG_SYNC_FILE_RANGE', cc.has_function('sync_file_range'))
|
||||
config_host_data.set('CONFIG_TIMERFD', cc.has_function('timerfd_create'))
|
||||
config_host_data.set('HAVE_COPY_FILE_RANGE', cc.has_function('copy_file_range'))
|
||||
config_host_data.set('HAVE_GETIFADDRS', cc.has_function('getifaddrs'))
|
||||
config_host_data.set('HAVE_OPENPTY', cc.has_function('openpty', dependencies: util))
|
||||
config_host_data.set('HAVE_STRCHRNUL', cc.has_function('strchrnul'))
|
||||
config_host_data.set('HAVE_SYSTEM_FUNCTION', cc.has_function('system', prefix: '#include <stdlib.h>'))
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "qemu/base64.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "commands-common.h"
|
||||
#include "block/nvme.h"
|
||||
|
||||
#ifdef HAVE_UTMPX
|
||||
#include <utmpx.h>
|
||||
|
@ -34,11 +35,8 @@
|
|||
#if defined(__linux__)
|
||||
#include <mntent.h>
|
||||
#include <linux/fs.h>
|
||||
#include <ifaddrs.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/socket.h>
|
||||
#include <net/if.h>
|
||||
#include <sys/statvfs.h>
|
||||
#include <linux/nvme_ioctl.h>
|
||||
|
||||
#ifdef CONFIG_LIBUDEV
|
||||
#include <libudev.h>
|
||||
|
@ -52,6 +50,17 @@
|
|||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_GETIFADDRS
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/socket.h>
|
||||
#include <net/if.h>
|
||||
#include <sys/types.h>
|
||||
#include <ifaddrs.h>
|
||||
#ifdef CONFIG_SOLARIS
|
||||
#include <sys/sockio.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
static void ga_wait_child(pid_t pid, int *status, Error **errp)
|
||||
{
|
||||
pid_t rpid;
|
||||
|
@ -78,13 +87,23 @@ void qmp_guest_shutdown(bool has_mode, const char *mode, Error **errp)
|
|||
pid_t pid;
|
||||
int status;
|
||||
|
||||
#ifdef CONFIG_SOLARIS
|
||||
const char *powerdown_flag = "-i5";
|
||||
const char *halt_flag = "-i0";
|
||||
const char *reboot_flag = "-i6";
|
||||
#else
|
||||
const char *powerdown_flag = "-P";
|
||||
const char *halt_flag = "-H";
|
||||
const char *reboot_flag = "-r";
|
||||
#endif
|
||||
|
||||
slog("guest-shutdown called, mode: %s", mode);
|
||||
if (!has_mode || strcmp(mode, "powerdown") == 0) {
|
||||
shutdown_flag = "-P";
|
||||
shutdown_flag = powerdown_flag;
|
||||
} else if (strcmp(mode, "halt") == 0) {
|
||||
shutdown_flag = "-H";
|
||||
shutdown_flag = halt_flag;
|
||||
} else if (strcmp(mode, "reboot") == 0) {
|
||||
shutdown_flag = "-r";
|
||||
shutdown_flag = reboot_flag;
|
||||
} else {
|
||||
error_setg(errp,
|
||||
"mode is invalid (valid values are: halt|powerdown|reboot");
|
||||
|
@ -99,8 +118,13 @@ void qmp_guest_shutdown(bool has_mode, const char *mode, Error **errp)
|
|||
reopen_fd_to_null(1);
|
||||
reopen_fd_to_null(2);
|
||||
|
||||
#ifdef CONFIG_SOLARIS
|
||||
execl("/sbin/shutdown", "shutdown", shutdown_flag, "-g0", "-y",
|
||||
"hypervisor initiated shutdown", (char *)NULL);
|
||||
#else
|
||||
execl("/sbin/shutdown", "shutdown", "-h", shutdown_flag, "+0",
|
||||
"hypervisor initiated shutdown", (char *)NULL);
|
||||
#endif
|
||||
_exit(EXIT_FAILURE);
|
||||
} else if (pid < 0) {
|
||||
error_setg_errno(errp, errno, "failed to create child process");
|
||||
|
@ -868,7 +892,8 @@ static bool build_guest_fsinfo_for_pci_dev(char const *syspath,
|
|||
if (driver && (g_str_equal(driver, "ata_piix") ||
|
||||
g_str_equal(driver, "sym53c8xx") ||
|
||||
g_str_equal(driver, "virtio-pci") ||
|
||||
g_str_equal(driver, "ahci"))) {
|
||||
g_str_equal(driver, "ahci") ||
|
||||
g_str_equal(driver, "nvme"))) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -963,6 +988,8 @@ static bool build_guest_fsinfo_for_pci_dev(char const *syspath,
|
|||
g_debug("no host for '%s' (driver '%s')", syspath, driver);
|
||||
goto cleanup;
|
||||
}
|
||||
} else if (strcmp(driver, "nvme") == 0) {
|
||||
disk->bus_type = GUEST_DISK_BUS_TYPE_NVME;
|
||||
} else {
|
||||
g_debug("unknown driver '%s' (sysfs path '%s')", driver, syspath);
|
||||
goto cleanup;
|
||||
|
@ -1366,6 +1393,76 @@ static GuestDiskInfoList *get_disk_partitions(
|
|||
return ret;
|
||||
}
|
||||
|
||||
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)
|
||||
};
|
||||
|
||||
fd = qemu_open_old(disk->name, O_RDONLY);
|
||||
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->has_smart = true;
|
||||
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)
|
||||
{
|
||||
if (disk->has_address
|
||||
&& (disk->address->bus_type == GUEST_DISK_BUS_TYPE_NVME)) {
|
||||
get_nvme_smart(disk);
|
||||
}
|
||||
}
|
||||
|
||||
GuestDiskInfoList *qmp_guest_get_disks(Error **errp)
|
||||
{
|
||||
GuestDiskInfoList *ret = NULL;
|
||||
|
@ -1439,6 +1536,7 @@ GuestDiskInfoList *qmp_guest_get_disks(Error **errp)
|
|||
}
|
||||
|
||||
get_disk_deps(disk_dir, disk);
|
||||
get_disk_smart(disk);
|
||||
ret = get_disk_partitions(ret, de->d_name, disk_dir, dev_name);
|
||||
}
|
||||
|
||||
|
@ -2133,223 +2231,6 @@ void qmp_guest_suspend_hybrid(Error **errp)
|
|||
guest_suspend(SUSPEND_MODE_HYBRID, errp);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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) {
|
||||
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);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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];
|
||||
int sock;
|
||||
struct ifreq ifr;
|
||||
unsigned char *mac_addr;
|
||||
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);
|
||||
}
|
||||
|
||||
if (!info->has_hardware_address && ifa->ifa_flags & SIOCGIFHWADDR) {
|
||||
/* 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");
|
||||
goto error;
|
||||
}
|
||||
|
||||
memset(&ifr, 0, sizeof(ifr));
|
||||
pstrcpy(ifr.ifr_name, IF_NAMESIZE, info->name);
|
||||
if (ioctl(sock, SIOCGIFHWADDR, &ifr) == -1) {
|
||||
error_setg_errno(errp, errno,
|
||||
"failed to get MAC address of %s",
|
||||
ifa->ifa_name);
|
||||
close(sock);
|
||||
goto error;
|
||||
}
|
||||
|
||||
close(sock);
|
||||
mac_addr = (unsigned char *) &ifr.ifr_hwaddr.sa_data;
|
||||
|
||||
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]);
|
||||
|
||||
info->has_hardware_address = true;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (!info->has_statistics) {
|
||||
interface_stat = g_malloc0(sizeof(*interface_stat));
|
||||
if (guest_get_network_stats(info->name, interface_stat) == -1) {
|
||||
info->has_statistics = false;
|
||||
g_free(interface_stat);
|
||||
} else {
|
||||
info->statistics = interface_stat;
|
||||
info->has_statistics = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
freeifaddrs(ifap);
|
||||
return head;
|
||||
|
||||
error:
|
||||
freeifaddrs(ifap);
|
||||
qapi_free_GuestNetworkInterfaceList(head);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Transfer online/offline status between @vcpu and the guest system.
|
||||
*
|
||||
* On input either @errp or *@errp must be NULL.
|
||||
|
@ -2919,12 +2800,6 @@ void qmp_guest_suspend_hybrid(Error **errp)
|
|||
error_setg(errp, QERR_UNSUPPORTED);
|
||||
}
|
||||
|
||||
GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp)
|
||||
{
|
||||
error_setg(errp, QERR_UNSUPPORTED);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp)
|
||||
{
|
||||
error_setg(errp, QERR_UNSUPPORTED);
|
||||
|
@ -2966,6 +2841,252 @@ GuestMemoryBlockInfo *qmp_guest_get_memory_block_info(Error **errp)
|
|||
|
||||
#endif
|
||||
|
||||
#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)
|
||||
{
|
||||
#ifdef CONFIG_LINUX
|
||||
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) {
|
||||
g_debug("failed to open network stats %s: %s", devinfo,
|
||||
g_strerror(errno));
|
||||
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);
|
||||
#else /* !CONFIG_LINUX */
|
||||
g_debug("Network stats reporting available only for Linux");
|
||||
#endif /* !CONFIG_LINUX */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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];
|
||||
int sock;
|
||||
struct ifreq ifr;
|
||||
unsigned char *mac_addr;
|
||||
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);
|
||||
}
|
||||
|
||||
if (!info->has_hardware_address) {
|
||||
/* 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");
|
||||
goto error;
|
||||
}
|
||||
|
||||
memset(&ifr, 0, sizeof(ifr));
|
||||
pstrcpy(ifr.ifr_name, IF_NAMESIZE, info->name);
|
||||
if (ioctl(sock, SIOCGIFHWADDR, &ifr) == -1) {
|
||||
/*
|
||||
* We can't get the hw addr of this interface, but that's not a
|
||||
* fatal error. Don't set info->hardware_address, but keep
|
||||
* going.
|
||||
*/
|
||||
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
|
||||
mac_addr = (unsigned char *) &ifr.ifr_addr.sa_data;
|
||||
#else
|
||||
mac_addr = (unsigned char *) &ifr.ifr_hwaddr.sa_data;
|
||||
#endif
|
||||
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]);
|
||||
|
||||
info->has_hardware_address = true;
|
||||
}
|
||||
close(sock);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (!info->has_statistics) {
|
||||
interface_stat = g_malloc0(sizeof(*interface_stat));
|
||||
if (guest_get_network_stats(info->name, interface_stat) == -1) {
|
||||
info->has_statistics = false;
|
||||
g_free(interface_stat);
|
||||
} else {
|
||||
info->statistics = interface_stat;
|
||||
info->has_statistics = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 */
|
||||
|
||||
#if !defined(CONFIG_FSFREEZE)
|
||||
|
||||
GuestFilesystemInfoList *qmp_guest_get_fsinfo(Error **errp)
|
||||
|
@ -3028,8 +3149,7 @@ GList *ga_command_blacklist_init(GList *blacklist)
|
|||
{
|
||||
const char *list[] = {
|
||||
"guest-suspend-disk", "guest-suspend-ram",
|
||||
"guest-suspend-hybrid", "guest-network-get-interfaces",
|
||||
"guest-get-vcpus", "guest-set-vcpus",
|
||||
"guest-suspend-hybrid", "guest-get-vcpus", "guest-set-vcpus",
|
||||
"guest-get-memory-blocks", "guest-set-memory-blocks",
|
||||
"guest-get-memory-block-size", "guest-get-memory-block-info",
|
||||
NULL};
|
||||
|
@ -3041,6 +3161,11 @@ GList *ga_command_blacklist_init(GList *blacklist)
|
|||
}
|
||||
#endif
|
||||
|
||||
#if !defined(HAVE_GETIFADDRS)
|
||||
blacklist = g_list_append(blacklist,
|
||||
g_strdup("guest-network-get-interfaces"));
|
||||
#endif
|
||||
|
||||
#if !defined(CONFIG_FSFREEZE)
|
||||
{
|
||||
const char *list[] = {
|
||||
|
|
|
@ -827,13 +827,14 @@
|
|||
# @mmc: Win multimedia card (MMC) bus type
|
||||
# @virtual: Win virtual bus type
|
||||
# @file-backed-virtual: Win file-backed bus type
|
||||
# @nvme: NVMe disks (since 7.1)
|
||||
#
|
||||
# Since: 2.2; 'Unknown' and all entries below since 2.4
|
||||
##
|
||||
{ 'enum': 'GuestDiskBusType',
|
||||
'data': [ 'ide', 'fdc', 'scsi', 'virtio', 'xen', 'usb', 'uml', 'sata',
|
||||
'sd', 'unknown', 'ieee1394', 'ssa', 'fibre', 'raid', 'iscsi',
|
||||
'sas', 'mmc', 'virtual', 'file-backed-virtual' ] }
|
||||
'sas', 'mmc', 'virtual', 'file-backed-virtual', 'nvme' ] }
|
||||
|
||||
|
||||
##
|
||||
|
@ -887,6 +888,55 @@
|
|||
'*serial': 'str', '*dev': 'str',
|
||||
'*ccw-address': 'GuestCCWAddress'} }
|
||||
|
||||
##
|
||||
# @GuestNVMeSmart:
|
||||
#
|
||||
# NVMe smart informations, based on NVMe specification,
|
||||
# section <SMART / Health Information (Log Identifier 02h)>
|
||||
#
|
||||
# Since: 7.1
|
||||
##
|
||||
{ 'struct': 'GuestNVMeSmart',
|
||||
'data': {'critical-warning': 'int',
|
||||
'temperature': 'int',
|
||||
'available-spare': 'int',
|
||||
'available-spare-threshold': 'int',
|
||||
'percentage-used': 'int',
|
||||
'data-units-read-lo': 'uint64',
|
||||
'data-units-read-hi': 'uint64',
|
||||
'data-units-written-lo': 'uint64',
|
||||
'data-units-written-hi': 'uint64',
|
||||
'host-read-commands-lo': 'uint64',
|
||||
'host-read-commands-hi': 'uint64',
|
||||
'host-write-commands-lo': 'uint64',
|
||||
'host-write-commands-hi': 'uint64',
|
||||
'controller-busy-time-lo': 'uint64',
|
||||
'controller-busy-time-hi': 'uint64',
|
||||
'power-cycles-lo': 'uint64',
|
||||
'power-cycles-hi': 'uint64',
|
||||
'power-on-hours-lo': 'uint64',
|
||||
'power-on-hours-hi': 'uint64',
|
||||
'unsafe-shutdowns-lo': 'uint64',
|
||||
'unsafe-shutdowns-hi': 'uint64',
|
||||
'media-errors-lo': 'uint64',
|
||||
'media-errors-hi': 'uint64',
|
||||
'number-of-error-log-entries-lo': 'uint64',
|
||||
'number-of-error-log-entries-hi': 'uint64' } }
|
||||
|
||||
##
|
||||
# @GuestDiskSmart:
|
||||
#
|
||||
# Disk type related smart information.
|
||||
#
|
||||
# - @nvme: NVMe disk smart
|
||||
#
|
||||
# Since: 7.1
|
||||
##
|
||||
{ 'union': 'GuestDiskSmart',
|
||||
'base': { 'type': 'GuestDiskBusType' },
|
||||
'discriminator': 'type',
|
||||
'data': { 'nvme': 'GuestNVMeSmart' } }
|
||||
|
||||
##
|
||||
# @GuestDiskInfo:
|
||||
#
|
||||
|
@ -898,12 +948,14 @@
|
|||
# @address: disk address information (only for non-virtual devices)
|
||||
# @alias: optional alias assigned to the disk, on Linux this is a name assigned
|
||||
# by device mapper
|
||||
# @smart: disk smart information (Since 7.1)
|
||||
#
|
||||
# Since 5.2
|
||||
##
|
||||
{ 'struct': 'GuestDiskInfo',
|
||||
'data': {'name': 'str', 'partition': 'bool', '*dependencies': ['str'],
|
||||
'*address': 'GuestDiskAddress', '*alias': 'str'} }
|
||||
'*address': 'GuestDiskAddress', '*alias': 'str',
|
||||
'*smart': 'GuestDiskSmart'} }
|
||||
|
||||
##
|
||||
# @guest-get-disks:
|
||||
|
|
Loading…
Reference in New Issue