#include "qemu/osdep.h"
#include <sys/ioctl.h>
+#include <sys/utsname.h>
#include <sys/wait.h>
#include <dirent.h>
-#include <utmpx.h>
-#include "qga/guest-agent-core.h"
-#include "qga-qmp-commands.h"
+#include "guest-agent-core.h"
+#include "qga-qapi-commands.h"
+#include "qapi/error.h"
#include "qapi/qmp/qerror.h"
#include "qemu/queue.h"
#include "qemu/host-utils.h"
#include "qemu/base64.h"
#include "qemu/cutils.h"
+#ifdef HAVE_UTMPX
+#include <utmpx.h>
+#endif
+
#ifndef CONFIG_HAS_ENVIRON
#ifdef __APPLE__
#include <crt_externs.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <net/if.h>
+#include <sys/statvfs.h>
+
+#ifdef CONFIG_LIBUDEV
+#include <libudev.h>
+#endif
#ifdef FIFREEZE
#define CONFIG_FSFREEZE
if (!has_count) {
count = QGA_READ_COUNT_DEFAULT;
- } else if (count < 0) {
+ } else if (count < 0 || count >= UINT32_MAX) {
error_setg(errp, "value '%" PRId64 "' is invalid for argument count",
count);
return NULL;
len = readlink(dpath, buf, sizeof(buf) - 1);
if (len != -1) {
buf[len] = 0;
- driver = g_strdup(basename(buf));
+ driver = g_path_get_basename(buf);
}
g_free(dpath);
g_free(path);
GuestDiskAddressList *list = NULL;
bool has_ata = false, has_host = false, has_tgt = false;
char *p, *q, *driver = NULL;
+#ifdef CONFIG_LIBUDEV
+ struct udev *udev = NULL;
+ struct udev_device *udevice = NULL;
+#endif
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) {
- g_debug("only pci device is supported: sysfs path \"%s\"", syspath);
+ g_debug("only pci device is supported: sysfs path '%s'", syspath);
return;
}
- driver = get_pci_driver(syspath, (p + 12 + pcilen) - syspath, errp);
- if (!driver) {
- goto cleanup;
+ 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") ||
+ g_str_equal(driver, "ahci"))) {
+ break;
+ }
+
+ g_free(driver);
+ 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);
+ return;
}
p = strstr(syspath, "/target");
if (p && sscanf(q, "%u", &host) == 1) {
has_host = true;
nhosts = build_hosts(syspath, p, has_ata, hosts,
- sizeof(hosts) / sizeof(hosts[0]), errp);
+ ARRAY_SIZE(hosts), errp);
if (nhosts < 0) {
goto cleanup;
}
list = g_malloc0(sizeof(*list));
list->value = disk;
+#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);
+ disk->has_dev = true;
+ }
+ serial = udev_device_get_property_value(udevice, "ID_SERIAL");
+ if (serial != NULL && *serial != 0) {
+ disk->serial = g_strdup(serial);
+ disk->has_serial = true;
+ }
+ }
+#endif
+
if (strcmp(driver, "ata_piix") == 0) {
/* a host per ide bus, target*:0:<unit>:0 */
if (!has_host || !has_tgt) {
list->next = fs->disk;
fs->disk = list;
- g_free(driver);
- return;
+ goto out;
cleanup:
if (list) {
qapi_free_GuestDiskAddressList(list);
}
+out:
g_free(driver);
+#ifdef CONFIG_LIBUDEV
+ udev_unref(udev);
+ udev_device_unref(udevice);
+#endif
+ return;
}
static void build_guest_fsinfo_for_device(char const *devpath,
}
if (!fs->name) {
- fs->name = g_strdup(basename(syspath));
+ fs->name = g_path_get_basename(syspath);
}
g_debug(" parse sysfs path '%s'", syspath);
Error **errp)
{
GuestFilesystemInfo *fs = g_malloc0(sizeof(*fs));
+ struct statvfs buf;
+ unsigned long used, nonroot_total, fr_size;
char *devpath = g_strdup_printf("/sys/dev/block/%u:%u",
mount->devmajor, mount->devminor);
fs->type = g_strdup(mount->devtype);
build_guest_fsinfo_for_device(devpath, fs, errp);
+ 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;
+ }
+
g_free(devpath);
+
return fs;
}
/* cannot risk guest agent blocking itself on a write in this state */
ga_set_frozen(ga_state);
- QTAILQ_FOREACH_REVERSE(mount, &mounts, FsMountList, next) {
+ QTAILQ_FOREACH_REVERSE(mount, &mounts, next) {
/* To issue fsfreeze in the reverse order of mounts, check if the
* mount is listed in the list here */
if (has_mountpoints) {
}
free_fs_mount_list(&mounts);
+ /* We may not issue any FIFREEZE here.
+ * Just unset ga_state here and ready for the next call.
+ */
+ if (i == 0) {
+ ga_unset_frozen(ga_state);
+ }
return i;
error:
#define SUSPEND_SUPPORTED 0
#define SUSPEND_NOT_SUPPORTED 1
-static void bios_supports_mode(const char *pmutils_bin, const char *pmutils_arg,
- const char *sysfile_str, Error **errp)
+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)
+{
+ 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;
+
+ success = g_spawn_sync(NULL, (char **)command, environ, spawn_flag,
+ NULL, NULL, NULL, NULL,
+ &exit_status, &g_err);
+
+ if (success) {
+ return WEXITSTATUS(exit_status);
+ }
+
+ if (g_err && (g_err->code != G_SPAWN_ERROR_NOENT)) {
+ error_setg(errp, "failed to create child process, error '%s'",
+ g_err->message);
+ }
+
+ g_error_free(g_err);
+ return -1;
+}
+
+static bool systemd_supports_mode(SuspendMode mode, Error **errp)
{
Error *local_err = NULL;
- char *pmutils_path;
- pid_t pid;
+ const char *systemctl_args[3] = {"systemd-hibernate", "systemd-suspend",
+ "systemd-hybrid-sleep"};
+ const char *cmd[4] = {"systemctl", "status", systemctl_args[mode], NULL};
int status;
- pmutils_path = g_find_program_in_path(pmutils_bin);
+ status = run_process_child(cmd, &local_err);
- pid = fork();
- if (!pid) {
- char buf[32]; /* hopefully big enough */
- ssize_t ret;
- int fd;
+ /*
+ * 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;
+ }
- setsid();
- reopen_fd_to_null(0);
- reopen_fd_to_null(1);
- reopen_fd_to_null(2);
+ error_propagate(errp, local_err);
+ return false;
+}
- if (pmutils_path) {
- execle(pmutils_path, pmutils_bin, pmutils_arg, NULL, environ);
- }
+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;
- /*
- * If we get here either pm-utils is not installed or execle() has
- * failed. Let's try the manual method if the caller wants it.
- */
+ status = run_process_child(cmd, &local_err);
- if (!sysfile_str) {
- _exit(SUSPEND_NOT_SUPPORTED);
- }
+ if (status == 0) {
+ return;
+ }
- fd = open(LINUX_SYS_STATE_FILE, O_RDONLY);
- if (fd < 0) {
- _exit(SUSPEND_NOT_SUPPORTED);
- }
+ if ((status == -1) && !local_err) {
+ error_setg(errp, "the helper program 'systemctl %s' was not found",
+ systemctl_args[mode]);
+ return;
+ }
- ret = read(fd, buf, sizeof(buf)-1);
- if (ret <= 0) {
- _exit(SUSPEND_NOT_SUPPORTED);
- }
- buf[ret] = '\0';
+ 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);
+ }
+}
- if (strstr(buf, sysfile_str)) {
- _exit(SUSPEND_SUPPORTED);
- }
+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;
- _exit(SUSPEND_NOT_SUPPORTED);
- } else if (pid < 0) {
- error_setg_errno(errp, errno, "failed to create child process");
- goto out;
+ status = run_process_child(cmd, &local_err);
+
+ if (status == SUSPEND_SUPPORTED) {
+ return true;
+ }
+
+ if ((status == -1) && !local_err) {
+ return false;
}
- ga_wait_child(pid, &status, &local_err);
if (local_err) {
error_propagate(errp, local_err);
- goto out;
+ } else {
+ error_setg(errp,
+ "the helper program '%s' returned an unexpected exit"
+ " status code (%d)", "pm-is-supported", status);
}
- if (!WIFEXITED(status)) {
- error_setg(errp, "child process has terminated abnormally");
- goto out;
+ return false;
+}
+
+static void pmutils_suspend(SuspendMode mode, Error **errp)
+{
+ Error *local_err = NULL;
+ const char *pmutils_binaries[3] = {"pm-hibernate", "pm-suspend",
+ "pm-suspend-hybrid"};
+ const char *cmd[2] = {pmutils_binaries[mode], NULL};
+ int status;
+
+ status = run_process_child(cmd, &local_err);
+
+ if (status == 0) {
+ return;
}
- switch (WEXITSTATUS(status)) {
- case SUSPEND_SUPPORTED:
- goto out;
- case SUSPEND_NOT_SUPPORTED:
- error_setg(errp,
- "the requested suspend mode is not supported by the guest");
- goto out;
- default:
+ if ((status == -1) && !local_err) {
+ error_setg(errp, "the helper program '%s' was not found",
+ pmutils_binaries[mode]);
+ return;
+ }
+
+ if (local_err) {
+ error_propagate(errp, local_err);
+ } else {
error_setg(errp,
- "the helper program '%s' returned an unexpected exit status"
- " code (%d)", pmutils_path, WEXITSTATUS(status));
- goto out;
+ "the helper program '%s' returned an unexpected exit"
+ " status code (%d)", pmutils_binaries[mode], status);
}
+}
-out:
- g_free(pmutils_path);
+static bool linux_sys_state_supports_mode(SuspendMode mode, Error **errp)
+{
+ const char *sysfile_strs[3] = {"disk", "mem", NULL};
+ const char *sysfile_str = sysfile_strs[mode];
+ char buf[32]; /* hopefully big enough */
+ int fd;
+ ssize_t ret;
+
+ if (!sysfile_str) {
+ error_setg(errp, "unknown guest suspend mode");
+ return false;
+ }
+
+ fd = open(LINUX_SYS_STATE_FILE, O_RDONLY);
+ if (fd < 0) {
+ return false;
+ }
+
+ ret = read(fd, buf, sizeof(buf) - 1);
+ close(fd);
+ if (ret <= 0) {
+ return false;
+ }
+ buf[ret] = '\0';
+
+ if (strstr(buf, sysfile_str)) {
+ return true;
+ }
+ return false;
}
-static void guest_suspend(const char *pmutils_bin, const char *sysfile_str,
- Error **errp)
+static void linux_sys_state_suspend(SuspendMode mode, Error **errp)
{
Error *local_err = NULL;
- char *pmutils_path;
+ const char *sysfile_strs[3] = {"disk", "mem", NULL};
+ const char *sysfile_str = sysfile_strs[mode];
pid_t pid;
int status;
- pmutils_path = g_find_program_in_path(pmutils_bin);
+ if (!sysfile_str) {
+ error_setg(errp, "unknown guest suspend mode");
+ return;
+ }
pid = fork();
- if (pid == 0) {
+ if (!pid) {
/* child */
int fd;
reopen_fd_to_null(1);
reopen_fd_to_null(2);
- if (pmutils_path) {
- execle(pmutils_path, pmutils_bin, NULL, environ);
- }
-
- /*
- * If we get here either pm-utils is not installed or execle() has
- * failed. Let's try the manual method if the caller wants it.
- */
-
- if (!sysfile_str) {
- _exit(EXIT_FAILURE);
- }
-
fd = open(LINUX_SYS_STATE_FILE, O_WRONLY);
if (fd < 0) {
_exit(EXIT_FAILURE);
_exit(EXIT_SUCCESS);
} else if (pid < 0) {
error_setg_errno(errp, errno, "failed to create child process");
- goto out;
+ return;
}
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;
+ return;
}
if (WEXITSTATUS(status)) {
error_setg(errp, "child process has failed to suspend");
- goto out;
}
-out:
- g_free(pmutils_path);
}
-void qmp_guest_suspend_disk(Error **errp)
+static void guest_suspend(SuspendMode mode, Error **errp)
{
Error *local_err = NULL;
+ bool mode_supported = false;
- bios_supports_mode("pm-is-supported", "--hibernate", "disk", &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
+ if (systemd_supports_mode(mode, &local_err)) {
+ mode_supported = true;
+ systemd_suspend(mode, &local_err);
+ }
+
+ if (!local_err) {
return;
}
- guest_suspend("pm-hibernate", "disk", errp);
-}
+ error_free(local_err);
-void qmp_guest_suspend_ram(Error **errp)
-{
- Error *local_err = NULL;
+ if (pmutils_supports_mode(mode, &local_err)) {
+ mode_supported = true;
+ pmutils_suspend(mode, &local_err);
+ }
- bios_supports_mode("pm-is-supported", "--suspend", "mem", &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
+ if (!local_err) {
return;
}
- guest_suspend("pm-suspend", "mem", errp);
-}
+ error_free(local_err);
-void qmp_guest_suspend_hybrid(Error **errp)
-{
- Error *local_err = NULL;
+ if (linux_sys_state_supports_mode(mode, &local_err)) {
+ mode_supported = true;
+ linux_sys_state_suspend(mode, &local_err);
+ }
- bios_supports_mode("pm-is-supported", "--suspend-hybrid", NULL,
- &local_err);
- if (local_err) {
+ if (!mode_supported) {
+ error_setg(errp,
+ "the requested suspend mode is not supported by the guest");
+ } else {
error_propagate(errp, local_err);
- return;
}
+}
- guest_suspend("pm-suspend-hybrid", NULL, errp);
+void qmp_guest_suspend_disk(Error **errp)
+{
+ guest_suspend(SUSPEND_MODE_DISK, errp);
+}
+
+void qmp_guest_suspend_ram(Error **errp)
+{
+ guest_suspend(SUSPEND_MODE_RAM, errp);
+}
+
+void qmp_guest_suspend_hybrid(Error **errp)
+{
+ guest_suspend(SUSPEND_MODE_HYBRID, errp);
}
static GuestNetworkInterfaceList *
return head;
}
+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
*/
for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
GuestNetworkInterfaceList *info;
GuestIpAddressList **address_list = NULL, *address_item = NULL;
+ GuestNetworkInterfaceStat *interface_stat = NULL;
char addr4[INET_ADDRSTRLEN];
char addr6[INET6_ADDRSTRLEN];
int sock;
info->value->has_ip_addresses = true;
-
+ if (!info->value->has_statistics) {
+ interface_stat = g_malloc0(sizeof(*interface_stat));
+ if (guest_get_network_stats(info->value->name,
+ interface_stat) == -1) {
+ info->value->has_statistics = false;
+ g_free(interface_stat);
+ } else {
+ info->value->statistics = interface_stat;
+ info->value->has_statistics = true;
+ }
+ }
}
freeifaddrs(ifap);
* Written members remain unmodified on error.
*/
static void transfer_vcpu(GuestLogicalProcessor *vcpu, bool sys2vcpu,
- Error **errp)
+ char *dirpath, Error **errp)
{
- char *dirpath;
+ int fd;
+ int res;
int dirfd;
+ static const char fn[] = "online";
- dirpath = g_strdup_printf("/sys/devices/system/cpu/cpu%" PRId64 "/",
- vcpu->logical_id);
dirfd = open(dirpath, O_RDONLY | O_DIRECTORY);
if (dirfd == -1) {
error_setg_errno(errp, errno, "open(\"%s\")", dirpath);
- } else {
- static const char fn[] = "online";
- int fd;
- int res;
-
- 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 */
+ return;
+ }
- res = close(fd);
- g_assert(res == 0);
- }
+ 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 */
- res = close(dirfd);
+ res = close(fd);
g_assert(res == 0);
}
- g_free(dirpath);
+ res = close(dirfd);
+ g_assert(res == 0);
}
GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp)
while (local_err == NULL && current < sc_max) {
GuestLogicalProcessor *vcpu;
GuestLogicalProcessorList *entry;
-
- vcpu = g_malloc0(sizeof *vcpu);
- vcpu->logical_id = current++;
- vcpu->has_can_offline = true; /* lolspeak ftw */
- transfer_vcpu(vcpu, true, &local_err);
-
- entry = g_malloc0(sizeof *entry);
- entry->value = vcpu;
-
- *link = entry;
- link = &entry->next;
+ int64_t id = current++;
+ char *path = g_strdup_printf("/sys/devices/system/cpu/cpu%" PRId64 "/",
+ id);
+
+ if (g_file_test(path, G_FILE_TEST_EXISTS)) {
+ vcpu = g_malloc0(sizeof *vcpu);
+ vcpu->logical_id = id;
+ vcpu->has_can_offline = true; /* lolspeak ftw */
+ transfer_vcpu(vcpu, true, path, &local_err);
+ entry = g_malloc0(sizeof *entry);
+ entry->value = vcpu;
+ *link = entry;
+ link = &entry->next;
+ }
+ g_free(path);
}
if (local_err == NULL) {
processed = 0;
while (vcpus != NULL) {
- transfer_vcpu(vcpus->value, false, &local_err);
+ 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);
if (local_err != NULL) {
break;
}
* we think this VM does not support online/offline memory block,
* any other solution?
*/
- if (!dp && errno == ENOENT) {
- result->response =
- GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_NOT_SUPPORTED;
+ if (!dp) {
+ if (errno == ENOENT) {
+ result->response =
+ GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_NOT_SUPPORTED;
+ }
goto out1;
}
closedir(dp);
}
} else {
if (mem_blk->online != (strncmp(status, "online", 6) == 0)) {
- char *new_state = mem_blk->online ? g_strdup("online") :
- g_strdup("offline");
+ const char *new_state = mem_blk->online ? "online" : "offline";
ga_write_sysfs_file(dirfd, "state", new_state, strlen(new_state),
&local_err);
- g_free(new_state);
if (local_err) {
error_free(local_err);
result->response =
#endif
}
+#ifdef HAVE_UTMPX
+
#define QGA_MICRO_SECOND_TO_SECOND 1000000
static double ga_get_login_time(struct utmpx *user_info)
g_hash_table_destroy(cache);
return head;
}
+
+#else
+
+GuestUserList *qmp_guest_get_users(Error **errp)
+{
+ error_setg(errp, QERR_UNSUPPORTED);
+ return NULL;
+}
+
+#endif
+
+/* 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;
+ GKeyFile *osrelease = NULL;
+ const char *qga_os_release = g_getenv("QGA_OS_RELEASE");
+
+ info = g_new0(GuestOSInfo, 1);
+
+ if (uname(&kinfo) != 0) {
+ error_setg_errno(errp, errno, "uname failed");
+ } else {
+ info->has_kernel_version = true;
+ info->kernel_version = g_strdup(kinfo.version);
+ info->has_kernel_release = true;
+ info->kernel_release = g_strdup(kinfo.release);
+ info->has_machine = true;
+ info->machine = g_strdup(kinfo.machine);
+ }
+
+ 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");
+ }
+ }
+
+ 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->has_ ## field = true; \
+ 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;
+}