#include <glib.h>
#include <sys/types.h>
#include <sys/ioctl.h>
+#include <sys/wait.h>
#include "qga/guest-agent-core.h"
#include "qga-qmp-commands.h"
-#include "qerror.h"
-#include "qemu-queue.h"
-#include "host-utils.h"
+#include "qapi/qmp/qerror.h"
+#include "qemu/queue.h"
+#include "qemu/host-utils.h"
+
+#ifndef CONFIG_HAS_ENVIRON
+#ifdef __APPLE__
+#include <crt_externs.h>
+#define environ (*_NSGetEnviron())
+#else
+extern char **environ;
+#endif
+#endif
#if defined(__linux__)
#include <mntent.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <net/if.h>
-#include <sys/wait.h>
-#if defined(__linux__) && defined(FIFREEZE)
+#ifdef FIFREEZE
#define CONFIG_FSFREEZE
#endif
+#ifdef FITRIM
+#define CONFIG_FSTRIM
+#endif
#endif
-#if defined(__linux__)
-/* TODO: use this in place of all post-fork() fclose(std*) callers */
-static void reopen_fd_to_null(int fd)
+static void ga_wait_child(pid_t pid, int *status, Error **err)
{
- int nullfd;
+ pid_t rpid;
- nullfd = open("/dev/null", O_RDWR);
- if (nullfd < 0) {
- return;
- }
+ *status = 0;
- dup2(nullfd, fd);
+ do {
+ rpid = waitpid(pid, status, 0);
+ } while (rpid == -1 && errno == EINTR);
- if (nullfd != fd) {
- close(nullfd);
+ if (rpid == -1) {
+ error_setg_errno(err, errno, "failed to wait for child (pid: %d)", pid);
+ return;
}
+
+ g_assert(rpid == pid);
}
-#endif /* defined(__linux__) */
void qmp_guest_shutdown(bool has_mode, const char *mode, Error **err)
{
- int ret;
const char *shutdown_flag;
+ Error *local_err = NULL;
+ pid_t pid;
+ int status;
slog("guest-shutdown called, mode: %s", mode);
if (!has_mode || strcmp(mode, "powerdown") == 0) {
} else if (strcmp(mode, "reboot") == 0) {
shutdown_flag = "-r";
} else {
- error_set(err, QERR_INVALID_PARAMETER_VALUE, "mode",
- "halt|powerdown|reboot");
+ error_setg(err,
+ "mode is invalid (valid values are: halt|powerdown|reboot");
return;
}
- ret = fork();
- if (ret == 0) {
+ pid = fork();
+ if (pid == 0) {
/* child, start the shutdown */
setsid();
- fclose(stdin);
- fclose(stdout);
- fclose(stderr);
-
- ret = execl("/sbin/shutdown", "shutdown", shutdown_flag, "+0",
- "hypervisor initiated shutdown", (char*)NULL);
- if (ret) {
- slog("guest-shutdown failed: %s", strerror(errno));
- }
- exit(!!ret);
- } else if (ret < 0) {
- error_set(err, QERR_UNDEFINED_ERROR);
+ reopen_fd_to_null(0);
+ reopen_fd_to_null(1);
+ reopen_fd_to_null(2);
+
+ execle("/sbin/shutdown", "shutdown", shutdown_flag, "+0",
+ "hypervisor initiated shutdown", (char*)NULL, environ);
+ _exit(EXIT_FAILURE);
+ } else if (pid < 0) {
+ error_setg_errno(err, errno, "failed to create child process");
+ return;
+ }
+
+ ga_wait_child(pid, &status, &local_err);
+ if (error_is_set(&local_err)) {
+ error_propagate(err, local_err);
+ return;
}
+
+ if (!WIFEXITED(status)) {
+ error_setg(err, "child process has terminated abnormally");
+ return;
+ }
+
+ if (WEXITSTATUS(status)) {
+ error_setg(err, "child process has failed to shutdown");
+ return;
+ }
+
+ /* succeded */
}
typedef struct GuestFileHandle {
QTAILQ_INSERT_TAIL(&guest_file_state.filehandles, gfh, next);
}
-static GuestFileHandle *guest_file_handle_find(int64_t id)
+static GuestFileHandle *guest_file_handle_find(int64_t id, Error **err)
{
GuestFileHandle *gfh;
}
}
+ error_setg(err, "handle '%" PRId64 "' has not been found", id);
return NULL;
}
slog("guest-file-open called, filepath: %s, mode: %s", path, mode);
fh = fopen(path, mode);
if (!fh) {
- error_set(err, QERR_OPEN_FILE_FAILED, path);
+ error_setg_errno(err, errno, "failed to open file '%s' (mode: '%s')",
+ path, mode);
return -1;
}
ret = fcntl(fd, F_GETFL);
ret = fcntl(fd, F_SETFL, ret | O_NONBLOCK);
if (ret == -1) {
- error_set(err, QERR_QGA_COMMAND_FAILED, "fcntl() failed");
+ error_setg_errno(err, errno, "failed to make file '%s' non-blocking",
+ path);
fclose(fh);
return -1;
}
void qmp_guest_file_close(int64_t handle, Error **err)
{
- GuestFileHandle *gfh = guest_file_handle_find(handle);
+ GuestFileHandle *gfh = guest_file_handle_find(handle, err);
int ret;
slog("guest-file-close called, handle: %ld", handle);
if (!gfh) {
- error_set(err, QERR_FD_NOT_FOUND, "handle");
return;
}
ret = fclose(gfh->fh);
- if (ret == -1) {
- error_set(err, QERR_QGA_COMMAND_FAILED, "fclose() failed");
+ if (ret == EOF) {
+ error_setg_errno(err, errno, "failed to close handle");
return;
}
struct GuestFileRead *qmp_guest_file_read(int64_t handle, bool has_count,
int64_t count, Error **err)
{
- GuestFileHandle *gfh = guest_file_handle_find(handle);
+ GuestFileHandle *gfh = guest_file_handle_find(handle, err);
GuestFileRead *read_data = NULL;
guchar *buf;
FILE *fh;
size_t read_count;
if (!gfh) {
- error_set(err, QERR_FD_NOT_FOUND, "handle");
return NULL;
}
if (!has_count) {
count = QGA_READ_COUNT_DEFAULT;
} else if (count < 0) {
- error_set(err, QERR_INVALID_PARAMETER, "count");
+ error_setg(err, "value '%" PRId64 "' is invalid for argument count",
+ count);
return NULL;
}
buf = g_malloc0(count+1);
read_count = fread(buf, 1, count, fh);
if (ferror(fh)) {
+ error_setg_errno(err, errno, "failed to read file");
slog("guest-file-read failed, handle: %ld", handle);
- error_set(err, QERR_QGA_COMMAND_FAILED, "fread() failed");
} else {
buf[read_count] = 0;
read_data = g_malloc0(sizeof(GuestFileRead));
guchar *buf;
gsize buf_len;
int write_count;
- GuestFileHandle *gfh = guest_file_handle_find(handle);
+ GuestFileHandle *gfh = guest_file_handle_find(handle, err);
FILE *fh;
if (!gfh) {
- error_set(err, QERR_FD_NOT_FOUND, "handle");
return NULL;
}
if (!has_count) {
count = buf_len;
} else if (count < 0 || count > buf_len) {
+ error_setg(err, "value '%" PRId64 "' is invalid for argument count",
+ count);
g_free(buf);
- error_set(err, QERR_INVALID_PARAMETER, "count");
return NULL;
}
write_count = fwrite(buf, 1, count, fh);
if (ferror(fh)) {
+ error_setg_errno(err, errno, "failed to write to file");
slog("guest-file-write failed, handle: %ld", handle);
- error_set(err, QERR_QGA_COMMAND_FAILED, "fwrite() error");
} else {
write_data = g_malloc0(sizeof(GuestFileWrite));
write_data->count = write_count;
struct GuestFileSeek *qmp_guest_file_seek(int64_t handle, int64_t offset,
int64_t whence, Error **err)
{
- GuestFileHandle *gfh = guest_file_handle_find(handle);
+ GuestFileHandle *gfh = guest_file_handle_find(handle, err);
GuestFileSeek *seek_data = NULL;
FILE *fh;
int ret;
if (!gfh) {
- error_set(err, QERR_FD_NOT_FOUND, "handle");
return NULL;
}
fh = gfh->fh;
ret = fseek(fh, offset, whence);
if (ret == -1) {
- error_set(err, QERR_QGA_COMMAND_FAILED, strerror(errno));
+ error_setg_errno(err, errno, "failed to seek file");
} else {
seek_data = g_malloc0(sizeof(GuestFileRead));
seek_data->position = ftell(fh);
void qmp_guest_file_flush(int64_t handle, Error **err)
{
- GuestFileHandle *gfh = guest_file_handle_find(handle);
+ GuestFileHandle *gfh = guest_file_handle_find(handle, err);
FILE *fh;
int ret;
if (!gfh) {
- error_set(err, QERR_FD_NOT_FOUND, "handle");
return;
}
fh = gfh->fh;
ret = fflush(fh);
if (ret == EOF) {
- error_set(err, QERR_QGA_COMMAND_FAILED, strerror(errno));
+ error_setg_errno(err, errno, "failed to flush file");
}
}
/* linux-specific implementations. avoid this if at all possible. */
#if defined(__linux__)
-#if defined(CONFIG_FSFREEZE)
+#if defined(CONFIG_FSFREEZE) || defined(CONFIG_FSTRIM)
+typedef struct FsMount {
+ char *dirname;
+ char *devtype;
+ QTAILQ_ENTRY(FsMount) next;
+} FsMount;
-static void disable_logging(void)
-{
- ga_disable_logging(ga_state);
-}
+typedef QTAILQ_HEAD(, FsMount) FsMountList;
-static void enable_logging(void)
+static void free_fs_mount_list(FsMountList *mounts)
{
- ga_enable_logging(ga_state);
+ FsMount *mount, *temp;
+
+ if (!mounts) {
+ return;
+ }
+
+ QTAILQ_FOREACH_SAFE(mount, mounts, next, temp) {
+ QTAILQ_REMOVE(mounts, mount, next);
+ g_free(mount->dirname);
+ g_free(mount->devtype);
+ g_free(mount);
+ }
}
-typedef struct GuestFsfreezeMount {
- char *dirname;
- char *devtype;
- QTAILQ_ENTRY(GuestFsfreezeMount) next;
-} GuestFsfreezeMount;
-
-struct {
- GuestFsfreezeStatus status;
- QTAILQ_HEAD(, GuestFsfreezeMount) mount_list;
-} guest_fsfreeze_state;
-
/*
* Walk the mount table and build a list of local file systems
*/
-static int guest_fsfreeze_build_mount_list(void)
+static void build_fs_mount_list(FsMountList *mounts, Error **err)
{
struct mntent *ment;
- GuestFsfreezeMount *mount, *temp;
- char const *mtab = MOUNTED;
+ FsMount *mount;
+ char const *mtab = "/proc/self/mounts";
FILE *fp;
- QTAILQ_FOREACH_SAFE(mount, &guest_fsfreeze_state.mount_list, next, temp) {
- QTAILQ_REMOVE(&guest_fsfreeze_state.mount_list, mount, next);
- g_free(mount->dirname);
- g_free(mount->devtype);
- g_free(mount);
- }
-
fp = setmntent(mtab, "r");
if (!fp) {
- g_warning("fsfreeze: unable to read mtab");
- return -1;
+ error_setg(err, "failed to open mtab file: '%s'", mtab);
+ return;
}
while ((ment = getmntent(fp))) {
continue;
}
- mount = g_malloc0(sizeof(GuestFsfreezeMount));
+ mount = g_malloc0(sizeof(FsMount));
mount->dirname = g_strdup(ment->mnt_dir);
mount->devtype = g_strdup(ment->mnt_type);
- QTAILQ_INSERT_TAIL(&guest_fsfreeze_state.mount_list, mount, next);
+ QTAILQ_INSERT_TAIL(mounts, mount, next);
}
endmntent(fp);
+}
+#endif
- return 0;
+#if defined(CONFIG_FSFREEZE)
+
+typedef enum {
+ FSFREEZE_HOOK_THAW = 0,
+ FSFREEZE_HOOK_FREEZE,
+} FsfreezeHookArg;
+
+const char *fsfreeze_hook_arg_string[] = {
+ "thaw",
+ "freeze",
+};
+
+static void execute_fsfreeze_hook(FsfreezeHookArg arg, Error **err)
+{
+ 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(err, 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);
+
+ execle(hook, hook, arg_str, NULL, environ);
+ _exit(EXIT_FAILURE);
+ } else if (pid < 0) {
+ error_setg_errno(err, errno, "failed to create child process");
+ return;
+ }
+
+ ga_wait_child(pid, &status, &local_err);
+ if (error_is_set(&local_err)) {
+ error_propagate(err, local_err);
+ return;
+ }
+
+ if (!WIFEXITED(status)) {
+ error_setg(err, "fsfreeze hook has terminated abnormally");
+ return;
+ }
+
+ status = WEXITSTATUS(status);
+ if (status) {
+ error_setg(err, "fsfreeze hook has failed with status %d", status);
+ return;
+ }
}
/*
*/
GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **err)
{
- return guest_fsfreeze_state.status;
+ if (ga_is_frozen(ga_state)) {
+ return GUEST_FSFREEZE_STATUS_FROZEN;
+ }
+
+ return GUEST_FSFREEZE_STATUS_THAWED;
}
/*
int64_t qmp_guest_fsfreeze_freeze(Error **err)
{
int ret = 0, i = 0;
- struct GuestFsfreezeMount *mount, *temp;
+ FsMountList mounts;
+ struct FsMount *mount;
+ Error *local_err = NULL;
int fd;
- char err_msg[512];
slog("guest-fsfreeze called");
- if (guest_fsfreeze_state.status == GUEST_FSFREEZE_STATUS_FROZEN) {
- return 0;
+ execute_fsfreeze_hook(FSFREEZE_HOOK_FREEZE, &local_err);
+ if (error_is_set(&local_err)) {
+ error_propagate(err, local_err);
+ return -1;
}
- ret = guest_fsfreeze_build_mount_list();
- if (ret < 0) {
- return ret;
+ QTAILQ_INIT(&mounts);
+ build_fs_mount_list(&mounts, &local_err);
+ if (error_is_set(&local_err)) {
+ error_propagate(err, local_err);
+ return -1;
}
/* cannot risk guest agent blocking itself on a write in this state */
- disable_logging();
+ ga_set_frozen(ga_state);
- QTAILQ_FOREACH_SAFE(mount, &guest_fsfreeze_state.mount_list, next, temp) {
+ QTAILQ_FOREACH(mount, &mounts, next) {
fd = qemu_open(mount->dirname, O_RDONLY);
if (fd == -1) {
- sprintf(err_msg, "failed to open %s, %s", mount->dirname, strerror(errno));
- error_set(err, QERR_QGA_COMMAND_FAILED, err_msg);
+ error_setg_errno(err, errno, "failed to open %s", mount->dirname);
goto error;
}
/* we try to cull filesytems we know won't work in advance, but other
* filesytems may not implement fsfreeze for less obvious reasons.
- * these will report EOPNOTSUPP, so we simply ignore them. when
- * thawing, these filesystems will return an EINVAL instead, due to
- * not being in a frozen state. Other filesystem-specific
- * errors may result in EINVAL, however, so the user should check the
- * number * of filesystems returned here against those returned by the
- * thaw operation to determine whether everything completed
- * successfully
+ * these will report EOPNOTSUPP. we simply ignore these when tallying
+ * the number of frozen filesystems.
+ *
+ * any other error means a failure to freeze a filesystem we
+ * expect to be freezable, so return an error in those cases
+ * and return system to thawed state.
*/
ret = ioctl(fd, FIFREEZE);
- if (ret < 0 && errno != EOPNOTSUPP) {
- sprintf(err_msg, "failed to freeze %s, %s", mount->dirname, strerror(errno));
- error_set(err, QERR_QGA_COMMAND_FAILED, err_msg);
- close(fd);
- goto error;
+ if (ret == -1) {
+ if (errno != EOPNOTSUPP) {
+ error_setg_errno(err, errno, "failed to freeze %s",
+ mount->dirname);
+ close(fd);
+ goto error;
+ }
+ } else {
+ i++;
}
close(fd);
-
- i++;
}
- guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_FROZEN;
+ free_fs_mount_list(&mounts);
return i;
error:
- if (i > 0) {
- qmp_guest_fsfreeze_thaw(NULL);
- }
+ free_fs_mount_list(&mounts);
+ qmp_guest_fsfreeze_thaw(NULL);
return 0;
}
int64_t qmp_guest_fsfreeze_thaw(Error **err)
{
int ret;
- GuestFsfreezeMount *mount, *temp;
- int fd, i = 0;
- bool has_error = false;
+ FsMountList mounts;
+ FsMount *mount;
+ int fd, i = 0, logged;
+ Error *local_err = NULL;
+
+ QTAILQ_INIT(&mounts);
+ build_fs_mount_list(&mounts, &local_err);
+ if (error_is_set(&local_err)) {
+ error_propagate(err, local_err);
+ return 0;
+ }
- QTAILQ_FOREACH_SAFE(mount, &guest_fsfreeze_state.mount_list, next, temp) {
+ QTAILQ_FOREACH(mount, &mounts, next) {
+ logged = false;
fd = qemu_open(mount->dirname, O_RDONLY);
if (fd == -1) {
- has_error = true;
- continue;
- }
- ret = ioctl(fd, FITHAW);
- if (ret < 0 && errno != EOPNOTSUPP && errno != EINVAL) {
- has_error = true;
- close(fd);
continue;
}
+ /* we have no way of knowing whether a filesystem was actually unfrozen
+ * as a result of a successful call to FITHAW, only that if an error
+ * was returned the filesystem was *not* unfrozen by that particular
+ * call.
+ *
+ * since multiple preceding FIFREEZEs require multiple calls to FITHAW
+ * to unfreeze, continuing issuing FITHAW until an error is returned,
+ * in which case either the filesystem is in an unfreezable state, or,
+ * more likely, it was thawed previously (and remains so afterward).
+ *
+ * also, since the most recent successful call is the one that did
+ * the actual unfreeze, we can use this to provide an accurate count
+ * of the number of filesystems unfrozen by guest-fsfreeze-thaw, which
+ * may * be useful for determining whether a filesystem was unfrozen
+ * during the freeze/thaw phase by a process other than qemu-ga.
+ */
+ do {
+ ret = ioctl(fd, FITHAW);
+ if (ret == 0 && !logged) {
+ i++;
+ logged = true;
+ }
+ } while (ret == 0);
close(fd);
- i++;
}
- if (has_error) {
- guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_ERROR;
- } else {
- guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_THAWED;
- }
- enable_logging();
- return i;
-}
+ ga_unset_frozen(ga_state);
+ free_fs_mount_list(&mounts);
-static void guest_fsfreeze_init(void)
-{
- guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_THAWED;
- QTAILQ_INIT(&guest_fsfreeze_state.mount_list);
+ execute_fsfreeze_hook(FSFREEZE_HOOK_THAW, err);
+
+ return i;
}
static void guest_fsfreeze_cleanup(void)
int64_t ret;
Error *err = NULL;
- if (guest_fsfreeze_state.status == GUEST_FSFREEZE_STATUS_FROZEN) {
+ if (ga_is_frozen(ga_state) == GUEST_FSFREEZE_STATUS_FROZEN) {
ret = qmp_guest_fsfreeze_thaw(&err);
if (ret < 0 || err) {
slog("failed to clean up frozen filesystems");
}
#endif /* CONFIG_FSFREEZE */
+#if defined(CONFIG_FSTRIM)
+/*
+ * Walk list of mounted file systems in the guest, and trim them.
+ */
+void qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **err)
+{
+ int ret = 0;
+ FsMountList mounts;
+ struct FsMount *mount;
+ int fd;
+ Error *local_err = NULL;
+ struct fstrim_range r = {
+ .start = 0,
+ .len = -1,
+ .minlen = has_minimum ? minimum : 0,
+ };
+
+ slog("guest-fstrim called");
+
+ QTAILQ_INIT(&mounts);
+ build_fs_mount_list(&mounts, &local_err);
+ if (error_is_set(&local_err)) {
+ error_propagate(err, local_err);
+ return;
+ }
+
+ QTAILQ_FOREACH(mount, &mounts, next) {
+ fd = qemu_open(mount->dirname, O_RDONLY);
+ if (fd == -1) {
+ error_setg_errno(err, errno, "failed to open %s", mount->dirname);
+ goto error;
+ }
+
+ /* We try to cull filesytems we know won't work in advance, but other
+ * filesytems may not implement fstrim for less obvious reasons. These
+ * will report EOPNOTSUPP; we simply ignore these errors. Any other
+ * error means an unexpected error, so return it in those cases. In
+ * some other cases ENOTTY will be reported (e.g. CD-ROMs).
+ */
+ ret = ioctl(fd, FITRIM, &r);
+ if (ret == -1) {
+ if (errno != ENOTTY && errno != EOPNOTSUPP) {
+ error_setg_errno(err, errno, "failed to trim %s",
+ mount->dirname);
+ close(fd);
+ goto error;
+ }
+ }
+ close(fd);
+ }
+
+error:
+ free_fs_mount_list(&mounts);
+}
+#endif /* CONFIG_FSTRIM */
+
+
#define LINUX_SYS_STATE_FILE "/sys/power/state"
#define SUSPEND_SUPPORTED 0
#define SUSPEND_NOT_SUPPORTED 1
-/**
- * This function forks twice and the information about the mode support
- * status is passed to the qemu-ga process via a pipe.
- *
- * This approach allows us to keep the way we reap terminated children
- * in qemu-ga quite simple.
- */
static void bios_supports_mode(const char *pmutils_bin, const char *pmutils_arg,
const char *sysfile_str, Error **err)
{
- pid_t pid;
- ssize_t ret;
+ Error *local_err = NULL;
char *pmutils_path;
- int status, pipefds[2];
-
- if (pipe(pipefds) < 0) {
- error_set(err, QERR_UNDEFINED_ERROR);
- return;
- }
+ pid_t pid;
+ int status;
pmutils_path = g_find_program_in_path(pmutils_bin);
pid = fork();
if (!pid) {
- struct sigaction act;
-
- memset(&act, 0, sizeof(act));
- act.sa_handler = SIG_DFL;
- sigaction(SIGCHLD, &act, NULL);
+ char buf[32]; /* hopefully big enough */
+ ssize_t ret;
+ int fd;
setsid();
- close(pipefds[0]);
reopen_fd_to_null(0);
reopen_fd_to_null(1);
reopen_fd_to_null(2);
- pid = fork();
- if (!pid) {
- int fd;
- char buf[32]; /* hopefully big enough */
-
- if (pmutils_path) {
- execle(pmutils_path, pmutils_bin, pmutils_arg, 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(SUSPEND_NOT_SUPPORTED);
- }
-
- fd = open(LINUX_SYS_STATE_FILE, O_RDONLY);
- if (fd < 0) {
- _exit(SUSPEND_NOT_SUPPORTED);
- }
+ if (pmutils_path) {
+ execle(pmutils_path, pmutils_bin, pmutils_arg, NULL, environ);
+ }
- ret = read(fd, buf, sizeof(buf)-1);
- if (ret <= 0) {
- _exit(SUSPEND_NOT_SUPPORTED);
- }
- buf[ret] = '\0';
+ /*
+ * 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 (strstr(buf, sysfile_str)) {
- _exit(SUSPEND_SUPPORTED);
- }
+ if (!sysfile_str) {
+ _exit(SUSPEND_NOT_SUPPORTED);
+ }
+ fd = open(LINUX_SYS_STATE_FILE, O_RDONLY);
+ if (fd < 0) {
_exit(SUSPEND_NOT_SUPPORTED);
}
- if (pid > 0) {
- wait(&status);
- } else {
- status = SUSPEND_NOT_SUPPORTED;
+ ret = read(fd, buf, sizeof(buf)-1);
+ if (ret <= 0) {
+ _exit(SUSPEND_NOT_SUPPORTED);
}
+ buf[ret] = '\0';
- ret = write(pipefds[1], &status, sizeof(status));
- if (ret != sizeof(status)) {
- _exit(EXIT_FAILURE);
+ if (strstr(buf, sysfile_str)) {
+ _exit(SUSPEND_SUPPORTED);
}
- _exit(EXIT_SUCCESS);
+ _exit(SUSPEND_NOT_SUPPORTED);
+ } else if (pid < 0) {
+ error_setg_errno(err, errno, "failed to create child process");
+ goto out;
}
- close(pipefds[1]);
- g_free(pmutils_path);
-
- if (pid < 0) {
- error_set(err, QERR_UNDEFINED_ERROR);
+ ga_wait_child(pid, &status, &local_err);
+ if (error_is_set(&local_err)) {
+ error_propagate(err, local_err);
goto out;
}
- ret = read(pipefds[0], &status, sizeof(status));
- if (ret == sizeof(status) && WIFEXITED(status) &&
- WEXITSTATUS(status) == SUSPEND_SUPPORTED) {
- goto out;
+ if (!WIFEXITED(status)) {
+ error_setg(err, "child process has terminated abnormally");
+ goto out;
}
- error_set(err, QERR_UNSUPPORTED);
+ switch (WEXITSTATUS(status)) {
+ case SUSPEND_SUPPORTED:
+ goto out;
+ case SUSPEND_NOT_SUPPORTED:
+ error_setg(err,
+ "the requested suspend mode is not supported by the guest");
+ goto out;
+ default:
+ error_setg(err,
+ "the helper program '%s' returned an unexpected exit status"
+ " code (%d)", pmutils_path, WEXITSTATUS(status));
+ goto out;
+ }
out:
- close(pipefds[0]);
+ g_free(pmutils_path);
}
static void guest_suspend(const char *pmutils_bin, const char *sysfile_str,
Error **err)
{
- pid_t pid;
+ Error *local_err = NULL;
char *pmutils_path;
+ pid_t pid;
+ int status;
pmutils_path = g_find_program_in_path(pmutils_bin);
}
_exit(EXIT_SUCCESS);
+ } else if (pid < 0) {
+ error_setg_errno(err, errno, "failed to create child process");
+ goto out;
}
- g_free(pmutils_path);
+ ga_wait_child(pid, &status, &local_err);
+ if (error_is_set(&local_err)) {
+ error_propagate(err, local_err);
+ goto out;
+ }
- if (pid < 0) {
- error_set(err, QERR_UNDEFINED_ERROR);
- return;
+ if (!WIFEXITED(status)) {
+ error_setg(err, "child process has terminated abnormally");
+ goto out;
+ }
+
+ if (WEXITSTATUS(status)) {
+ error_setg(err, "child process has failed to suspend");
+ goto out;
}
+
+out:
+ g_free(pmutils_path);
}
void qmp_guest_suspend_disk(Error **err)
{
GuestNetworkInterfaceList *head = NULL, *cur_item = NULL;
struct ifaddrs *ifap, *ifa;
- char err_msg[512];
if (getifaddrs(&ifap) < 0) {
- snprintf(err_msg, sizeof(err_msg),
- "getifaddrs failed: %s", strerror(errno));
- error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg);
+ error_setg_errno(errp, errno, "getifaddrs failed");
goto error;
}
/* we haven't obtained HW address yet */
sock = socket(PF_INET, SOCK_STREAM, 0);
if (sock == -1) {
- snprintf(err_msg, sizeof(err_msg),
- "failed to create socket: %s", strerror(errno));
- error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg);
+ error_setg_errno(errp, errno, "failed to create socket");
goto error;
}
memset(&ifr, 0, sizeof(ifr));
- strncpy(ifr.ifr_name, info->value->name, IF_NAMESIZE);
+ pstrcpy(ifr.ifr_name, IF_NAMESIZE, info->value->name);
if (ioctl(sock, SIOCGIFHWADDR, &ifr) == -1) {
- snprintf(err_msg, sizeof(err_msg),
- "failed to get MAC addres of %s: %s",
- ifa->ifa_name,
- strerror(errno));
- error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg);
+ error_setg_errno(errp, errno,
+ "failed to get MAC address of %s",
+ ifa->ifa_name);
goto error;
}
(int) mac_addr[0], (int) mac_addr[1],
(int) mac_addr[2], (int) mac_addr[3],
(int) mac_addr[4], (int) mac_addr[5]) == -1) {
- snprintf(err_msg, sizeof(err_msg),
- "failed to format MAC: %s", strerror(errno));
- error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg);
+ error_setg_errno(errp, errno, "failed to format MAC");
goto error;
}
address_item->value = g_malloc0(sizeof(*address_item->value));
p = &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
if (!inet_ntop(AF_INET, p, addr4, sizeof(addr4))) {
- snprintf(err_msg, sizeof(err_msg),
- "inet_ntop failed : %s", strerror(errno));
- error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg);
+ error_setg_errno(errp, errno, "inet_ntop failed");
goto error;
}
address_item->value = g_malloc0(sizeof(*address_item->value));
p = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
if (!inet_ntop(AF_INET6, p, addr6, sizeof(addr6))) {
- snprintf(err_msg, sizeof(err_msg),
- "inet_ntop failed : %s", strerror(errno));
- error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg);
+ error_setg_errno(errp, errno, "inet_ntop failed");
goto error;
}
return 0;
}
+#endif /* CONFIG_FSFREEZE */
+#if !defined(CONFIG_FSTRIM)
+void qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **err)
+{
+ error_set(err, QERR_UNSUPPORTED);
+}
#endif
/* register init/cleanup routines for stateful command groups */
void ga_command_state_init(GAState *s, GACommandState *cs)
{
#if defined(CONFIG_FSFREEZE)
- ga_command_state_add(cs, guest_fsfreeze_init, guest_fsfreeze_cleanup);
+ ga_command_state_add(cs, NULL, guest_fsfreeze_cleanup);
#endif
ga_command_state_add(cs, guest_file_init, NULL);
}