aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Henderson <richard.henderson@linaro.org>2022-05-04 03:42:49 -0700
committerRichard Henderson <richard.henderson@linaro.org>2022-05-04 03:42:49 -0700
commit9cf289af47bcfae5c75de37d8e5d6fd23705322c (patch)
treefa48d3da9af891089afdae9a0568c208f4a063ca
parent2e3408b3cc7de4e87a9adafc8c19bfce3abec947 (diff)
parent22668881f3def13e9ffcf16840e5fde974a55b1e (diff)
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>
-rw-r--r--meson.build1
-rw-r--r--qga/commands-posix.c591
-rw-r--r--qga/qapi-schema.json56
3 files changed, 413 insertions, 235 deletions
diff --git a/meson.build b/meson.build
index 1fe7d257ff..c26aa442d4 100644
--- a/meson.build
+++ b/meson.build
@@ -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>'))
diff --git a/qga/commands-posix.c b/qga/commands-posix.c
index 78f2f21001..69f209af87 100644
--- a/qga/commands-posix.c
+++ b/qga/commands-posix.c
@@ -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[] = {
diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
index 94e4aacdcc..4d8e506c9e 100644
--- a/qga/qapi-schema.json
+++ b/qga/qapi-schema.json
@@ -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' ] }
##
@@ -888,6 +889,55 @@
'*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:
#
# @name: device node (Linux) or device UNC (Windows)
@@ -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: