1c216e5adSMichael Roth /* 2c216e5adSMichael Roth * QEMU Guest Agent POSIX-specific command implementations 3c216e5adSMichael Roth * 4c216e5adSMichael Roth * Copyright IBM Corp. 2011 5c216e5adSMichael Roth * 6c216e5adSMichael Roth * Authors: 7c216e5adSMichael Roth * Michael Roth <mdroth@linux.vnet.ibm.com> 8c216e5adSMichael Roth * 9c216e5adSMichael Roth * This work is licensed under the terms of the GNU GPL, version 2 or later. 10c216e5adSMichael Roth * See the COPYING file in the top-level directory. 11c216e5adSMichael Roth */ 12c216e5adSMichael Roth 13c216e5adSMichael Roth #include <glib.h> 14c216e5adSMichael Roth 15c216e5adSMichael Roth #if defined(__linux__) 16c216e5adSMichael Roth #include <mntent.h> 17c216e5adSMichael Roth #include <linux/fs.h> 18c216e5adSMichael Roth 19c216e5adSMichael Roth #if defined(__linux__) && defined(FIFREEZE) 20c216e5adSMichael Roth #define CONFIG_FSFREEZE 21c216e5adSMichael Roth #endif 22c216e5adSMichael Roth #endif 23c216e5adSMichael Roth 24c216e5adSMichael Roth #include <sys/types.h> 25c216e5adSMichael Roth #include <sys/ioctl.h> 2611d0f125SLuiz Capitulino #include <sys/wait.h> 27c216e5adSMichael Roth #include "qga/guest-agent-core.h" 28c216e5adSMichael Roth #include "qga-qmp-commands.h" 29c216e5adSMichael Roth #include "qerror.h" 30c216e5adSMichael Roth #include "qemu-queue.h" 31c216e5adSMichael Roth 32c216e5adSMichael Roth static GAState *ga_state; 33c216e5adSMichael Roth 3411d0f125SLuiz Capitulino static void reopen_fd_to_null(int fd) 3511d0f125SLuiz Capitulino { 3611d0f125SLuiz Capitulino int nullfd; 3711d0f125SLuiz Capitulino 3811d0f125SLuiz Capitulino nullfd = open("/dev/null", O_RDWR); 3911d0f125SLuiz Capitulino if (nullfd < 0) { 4011d0f125SLuiz Capitulino return; 4111d0f125SLuiz Capitulino } 4211d0f125SLuiz Capitulino 4311d0f125SLuiz Capitulino dup2(nullfd, fd); 4411d0f125SLuiz Capitulino 4511d0f125SLuiz Capitulino if (nullfd != fd) { 4611d0f125SLuiz Capitulino close(nullfd); 4711d0f125SLuiz Capitulino } 4811d0f125SLuiz Capitulino } 4911d0f125SLuiz Capitulino 50c216e5adSMichael Roth void qmp_guest_shutdown(bool has_mode, const char *mode, Error **err) 51c216e5adSMichael Roth { 52c216e5adSMichael Roth int ret; 53c216e5adSMichael Roth const char *shutdown_flag; 54c216e5adSMichael Roth 55c216e5adSMichael Roth slog("guest-shutdown called, mode: %s", mode); 56c216e5adSMichael Roth if (!has_mode || strcmp(mode, "powerdown") == 0) { 57c216e5adSMichael Roth shutdown_flag = "-P"; 58c216e5adSMichael Roth } else if (strcmp(mode, "halt") == 0) { 59c216e5adSMichael Roth shutdown_flag = "-H"; 60c216e5adSMichael Roth } else if (strcmp(mode, "reboot") == 0) { 61c216e5adSMichael Roth shutdown_flag = "-r"; 62c216e5adSMichael Roth } else { 63c216e5adSMichael Roth error_set(err, QERR_INVALID_PARAMETER_VALUE, "mode", 64c216e5adSMichael Roth "halt|powerdown|reboot"); 65c216e5adSMichael Roth return; 66c216e5adSMichael Roth } 67c216e5adSMichael Roth 68c216e5adSMichael Roth ret = fork(); 69c216e5adSMichael Roth if (ret == 0) { 70c216e5adSMichael Roth /* child, start the shutdown */ 71c216e5adSMichael Roth setsid(); 72c216e5adSMichael Roth fclose(stdin); 73c216e5adSMichael Roth fclose(stdout); 74c216e5adSMichael Roth fclose(stderr); 75c216e5adSMichael Roth 76c216e5adSMichael Roth ret = execl("/sbin/shutdown", "shutdown", shutdown_flag, "+0", 77c216e5adSMichael Roth "hypervisor initiated shutdown", (char*)NULL); 78c216e5adSMichael Roth if (ret) { 79c216e5adSMichael Roth slog("guest-shutdown failed: %s", strerror(errno)); 80c216e5adSMichael Roth } 81c216e5adSMichael Roth exit(!!ret); 82c216e5adSMichael Roth } else if (ret < 0) { 83c216e5adSMichael Roth error_set(err, QERR_UNDEFINED_ERROR); 84c216e5adSMichael Roth } 85c216e5adSMichael Roth } 86c216e5adSMichael Roth 87c216e5adSMichael Roth typedef struct GuestFileHandle { 88c216e5adSMichael Roth uint64_t id; 89c216e5adSMichael Roth FILE *fh; 90c216e5adSMichael Roth QTAILQ_ENTRY(GuestFileHandle) next; 91c216e5adSMichael Roth } GuestFileHandle; 92c216e5adSMichael Roth 93c216e5adSMichael Roth static struct { 94c216e5adSMichael Roth QTAILQ_HEAD(, GuestFileHandle) filehandles; 95c216e5adSMichael Roth } guest_file_state; 96c216e5adSMichael Roth 97c216e5adSMichael Roth static void guest_file_handle_add(FILE *fh) 98c216e5adSMichael Roth { 99c216e5adSMichael Roth GuestFileHandle *gfh; 100c216e5adSMichael Roth 101c216e5adSMichael Roth gfh = g_malloc0(sizeof(GuestFileHandle)); 102c216e5adSMichael Roth gfh->id = fileno(fh); 103c216e5adSMichael Roth gfh->fh = fh; 104c216e5adSMichael Roth QTAILQ_INSERT_TAIL(&guest_file_state.filehandles, gfh, next); 105c216e5adSMichael Roth } 106c216e5adSMichael Roth 107c216e5adSMichael Roth static GuestFileHandle *guest_file_handle_find(int64_t id) 108c216e5adSMichael Roth { 109c216e5adSMichael Roth GuestFileHandle *gfh; 110c216e5adSMichael Roth 111c216e5adSMichael Roth QTAILQ_FOREACH(gfh, &guest_file_state.filehandles, next) 112c216e5adSMichael Roth { 113c216e5adSMichael Roth if (gfh->id == id) { 114c216e5adSMichael Roth return gfh; 115c216e5adSMichael Roth } 116c216e5adSMichael Roth } 117c216e5adSMichael Roth 118c216e5adSMichael Roth return NULL; 119c216e5adSMichael Roth } 120c216e5adSMichael Roth 121c216e5adSMichael Roth int64_t qmp_guest_file_open(const char *path, bool has_mode, const char *mode, Error **err) 122c216e5adSMichael Roth { 123c216e5adSMichael Roth FILE *fh; 124c216e5adSMichael Roth int fd; 125c216e5adSMichael Roth int64_t ret = -1; 126c216e5adSMichael Roth 127c216e5adSMichael Roth if (!has_mode) { 128c216e5adSMichael Roth mode = "r"; 129c216e5adSMichael Roth } 130c216e5adSMichael Roth slog("guest-file-open called, filepath: %s, mode: %s", path, mode); 131c216e5adSMichael Roth fh = fopen(path, mode); 132c216e5adSMichael Roth if (!fh) { 133c216e5adSMichael Roth error_set(err, QERR_OPEN_FILE_FAILED, path); 134c216e5adSMichael Roth return -1; 135c216e5adSMichael Roth } 136c216e5adSMichael Roth 137c216e5adSMichael Roth /* set fd non-blocking to avoid common use cases (like reading from a 138c216e5adSMichael Roth * named pipe) from hanging the agent 139c216e5adSMichael Roth */ 140c216e5adSMichael Roth fd = fileno(fh); 141c216e5adSMichael Roth ret = fcntl(fd, F_GETFL); 142c216e5adSMichael Roth ret = fcntl(fd, F_SETFL, ret | O_NONBLOCK); 143c216e5adSMichael Roth if (ret == -1) { 144c216e5adSMichael Roth error_set(err, QERR_QGA_COMMAND_FAILED, "fcntl() failed"); 145c216e5adSMichael Roth fclose(fh); 146c216e5adSMichael Roth return -1; 147c216e5adSMichael Roth } 148c216e5adSMichael Roth 149c216e5adSMichael Roth guest_file_handle_add(fh); 150c216e5adSMichael Roth slog("guest-file-open, handle: %d", fd); 151c216e5adSMichael Roth return fd; 152c216e5adSMichael Roth } 153c216e5adSMichael Roth 154c216e5adSMichael Roth void qmp_guest_file_close(int64_t handle, Error **err) 155c216e5adSMichael Roth { 156c216e5adSMichael Roth GuestFileHandle *gfh = guest_file_handle_find(handle); 157c216e5adSMichael Roth int ret; 158c216e5adSMichael Roth 159c216e5adSMichael Roth slog("guest-file-close called, handle: %ld", handle); 160c216e5adSMichael Roth if (!gfh) { 161c216e5adSMichael Roth error_set(err, QERR_FD_NOT_FOUND, "handle"); 162c216e5adSMichael Roth return; 163c216e5adSMichael Roth } 164c216e5adSMichael Roth 165c216e5adSMichael Roth ret = fclose(gfh->fh); 166c216e5adSMichael Roth if (ret == -1) { 167c216e5adSMichael Roth error_set(err, QERR_QGA_COMMAND_FAILED, "fclose() failed"); 168c216e5adSMichael Roth return; 169c216e5adSMichael Roth } 170c216e5adSMichael Roth 171c216e5adSMichael Roth QTAILQ_REMOVE(&guest_file_state.filehandles, gfh, next); 172c216e5adSMichael Roth g_free(gfh); 173c216e5adSMichael Roth } 174c216e5adSMichael Roth 175c216e5adSMichael Roth struct GuestFileRead *qmp_guest_file_read(int64_t handle, bool has_count, 176c216e5adSMichael Roth int64_t count, Error **err) 177c216e5adSMichael Roth { 178c216e5adSMichael Roth GuestFileHandle *gfh = guest_file_handle_find(handle); 179c216e5adSMichael Roth GuestFileRead *read_data = NULL; 180c216e5adSMichael Roth guchar *buf; 181c216e5adSMichael Roth FILE *fh; 182c216e5adSMichael Roth size_t read_count; 183c216e5adSMichael Roth 184c216e5adSMichael Roth if (!gfh) { 185c216e5adSMichael Roth error_set(err, QERR_FD_NOT_FOUND, "handle"); 186c216e5adSMichael Roth return NULL; 187c216e5adSMichael Roth } 188c216e5adSMichael Roth 189c216e5adSMichael Roth if (!has_count) { 190c216e5adSMichael Roth count = QGA_READ_COUNT_DEFAULT; 191c216e5adSMichael Roth } else if (count < 0) { 192c216e5adSMichael Roth error_set(err, QERR_INVALID_PARAMETER, "count"); 193c216e5adSMichael Roth return NULL; 194c216e5adSMichael Roth } 195c216e5adSMichael Roth 196c216e5adSMichael Roth fh = gfh->fh; 197c216e5adSMichael Roth buf = g_malloc0(count+1); 198c216e5adSMichael Roth read_count = fread(buf, 1, count, fh); 199c216e5adSMichael Roth if (ferror(fh)) { 200c216e5adSMichael Roth slog("guest-file-read failed, handle: %ld", handle); 201c216e5adSMichael Roth error_set(err, QERR_QGA_COMMAND_FAILED, "fread() failed"); 202c216e5adSMichael Roth } else { 203c216e5adSMichael Roth buf[read_count] = 0; 204c216e5adSMichael Roth read_data = g_malloc0(sizeof(GuestFileRead)); 205c216e5adSMichael Roth read_data->count = read_count; 206c216e5adSMichael Roth read_data->eof = feof(fh); 207c216e5adSMichael Roth if (read_count) { 208c216e5adSMichael Roth read_data->buf_b64 = g_base64_encode(buf, read_count); 209c216e5adSMichael Roth } 210c216e5adSMichael Roth } 211c216e5adSMichael Roth g_free(buf); 212c216e5adSMichael Roth clearerr(fh); 213c216e5adSMichael Roth 214c216e5adSMichael Roth return read_data; 215c216e5adSMichael Roth } 216c216e5adSMichael Roth 217c216e5adSMichael Roth GuestFileWrite *qmp_guest_file_write(int64_t handle, const char *buf_b64, 218c216e5adSMichael Roth bool has_count, int64_t count, Error **err) 219c216e5adSMichael Roth { 220c216e5adSMichael Roth GuestFileWrite *write_data = NULL; 221c216e5adSMichael Roth guchar *buf; 222c216e5adSMichael Roth gsize buf_len; 223c216e5adSMichael Roth int write_count; 224c216e5adSMichael Roth GuestFileHandle *gfh = guest_file_handle_find(handle); 225c216e5adSMichael Roth FILE *fh; 226c216e5adSMichael Roth 227c216e5adSMichael Roth if (!gfh) { 228c216e5adSMichael Roth error_set(err, QERR_FD_NOT_FOUND, "handle"); 229c216e5adSMichael Roth return NULL; 230c216e5adSMichael Roth } 231c216e5adSMichael Roth 232c216e5adSMichael Roth fh = gfh->fh; 233c216e5adSMichael Roth buf = g_base64_decode(buf_b64, &buf_len); 234c216e5adSMichael Roth 235c216e5adSMichael Roth if (!has_count) { 236c216e5adSMichael Roth count = buf_len; 237c216e5adSMichael Roth } else if (count < 0 || count > buf_len) { 238c216e5adSMichael Roth g_free(buf); 239c216e5adSMichael Roth error_set(err, QERR_INVALID_PARAMETER, "count"); 240c216e5adSMichael Roth return NULL; 241c216e5adSMichael Roth } 242c216e5adSMichael Roth 243c216e5adSMichael Roth write_count = fwrite(buf, 1, count, fh); 244c216e5adSMichael Roth if (ferror(fh)) { 245c216e5adSMichael Roth slog("guest-file-write failed, handle: %ld", handle); 246c216e5adSMichael Roth error_set(err, QERR_QGA_COMMAND_FAILED, "fwrite() error"); 247c216e5adSMichael Roth } else { 248c216e5adSMichael Roth write_data = g_malloc0(sizeof(GuestFileWrite)); 249c216e5adSMichael Roth write_data->count = write_count; 250c216e5adSMichael Roth write_data->eof = feof(fh); 251c216e5adSMichael Roth } 252c216e5adSMichael Roth g_free(buf); 253c216e5adSMichael Roth clearerr(fh); 254c216e5adSMichael Roth 255c216e5adSMichael Roth return write_data; 256c216e5adSMichael Roth } 257c216e5adSMichael Roth 258c216e5adSMichael Roth struct GuestFileSeek *qmp_guest_file_seek(int64_t handle, int64_t offset, 259c216e5adSMichael Roth int64_t whence, Error **err) 260c216e5adSMichael Roth { 261c216e5adSMichael Roth GuestFileHandle *gfh = guest_file_handle_find(handle); 262c216e5adSMichael Roth GuestFileSeek *seek_data = NULL; 263c216e5adSMichael Roth FILE *fh; 264c216e5adSMichael Roth int ret; 265c216e5adSMichael Roth 266c216e5adSMichael Roth if (!gfh) { 267c216e5adSMichael Roth error_set(err, QERR_FD_NOT_FOUND, "handle"); 268c216e5adSMichael Roth return NULL; 269c216e5adSMichael Roth } 270c216e5adSMichael Roth 271c216e5adSMichael Roth fh = gfh->fh; 272c216e5adSMichael Roth ret = fseek(fh, offset, whence); 273c216e5adSMichael Roth if (ret == -1) { 274c216e5adSMichael Roth error_set(err, QERR_QGA_COMMAND_FAILED, strerror(errno)); 275c216e5adSMichael Roth } else { 276c216e5adSMichael Roth seek_data = g_malloc0(sizeof(GuestFileRead)); 277c216e5adSMichael Roth seek_data->position = ftell(fh); 278c216e5adSMichael Roth seek_data->eof = feof(fh); 279c216e5adSMichael Roth } 280c216e5adSMichael Roth clearerr(fh); 281c216e5adSMichael Roth 282c216e5adSMichael Roth return seek_data; 283c216e5adSMichael Roth } 284c216e5adSMichael Roth 285c216e5adSMichael Roth void qmp_guest_file_flush(int64_t handle, Error **err) 286c216e5adSMichael Roth { 287c216e5adSMichael Roth GuestFileHandle *gfh = guest_file_handle_find(handle); 288c216e5adSMichael Roth FILE *fh; 289c216e5adSMichael Roth int ret; 290c216e5adSMichael Roth 291c216e5adSMichael Roth if (!gfh) { 292c216e5adSMichael Roth error_set(err, QERR_FD_NOT_FOUND, "handle"); 293c216e5adSMichael Roth return; 294c216e5adSMichael Roth } 295c216e5adSMichael Roth 296c216e5adSMichael Roth fh = gfh->fh; 297c216e5adSMichael Roth ret = fflush(fh); 298c216e5adSMichael Roth if (ret == EOF) { 299c216e5adSMichael Roth error_set(err, QERR_QGA_COMMAND_FAILED, strerror(errno)); 300c216e5adSMichael Roth } 301c216e5adSMichael Roth } 302c216e5adSMichael Roth 303c216e5adSMichael Roth static void guest_file_init(void) 304c216e5adSMichael Roth { 305c216e5adSMichael Roth QTAILQ_INIT(&guest_file_state.filehandles); 306c216e5adSMichael Roth } 307c216e5adSMichael Roth 308c216e5adSMichael Roth #if defined(CONFIG_FSFREEZE) 309c216e5adSMichael Roth static void disable_logging(void) 310c216e5adSMichael Roth { 311c216e5adSMichael Roth ga_disable_logging(ga_state); 312c216e5adSMichael Roth } 313c216e5adSMichael Roth 314c216e5adSMichael Roth static void enable_logging(void) 315c216e5adSMichael Roth { 316c216e5adSMichael Roth ga_enable_logging(ga_state); 317c216e5adSMichael Roth } 318c216e5adSMichael Roth 319c216e5adSMichael Roth typedef struct GuestFsfreezeMount { 320c216e5adSMichael Roth char *dirname; 321c216e5adSMichael Roth char *devtype; 322c216e5adSMichael Roth QTAILQ_ENTRY(GuestFsfreezeMount) next; 323c216e5adSMichael Roth } GuestFsfreezeMount; 324c216e5adSMichael Roth 325c216e5adSMichael Roth struct { 326c216e5adSMichael Roth GuestFsfreezeStatus status; 327c216e5adSMichael Roth QTAILQ_HEAD(, GuestFsfreezeMount) mount_list; 328c216e5adSMichael Roth } guest_fsfreeze_state; 329c216e5adSMichael Roth 330c216e5adSMichael Roth /* 331c216e5adSMichael Roth * Walk the mount table and build a list of local file systems 332c216e5adSMichael Roth */ 333c216e5adSMichael Roth static int guest_fsfreeze_build_mount_list(void) 334c216e5adSMichael Roth { 335c216e5adSMichael Roth struct mntent *ment; 336c216e5adSMichael Roth GuestFsfreezeMount *mount, *temp; 337c216e5adSMichael Roth char const *mtab = MOUNTED; 338c216e5adSMichael Roth FILE *fp; 339c216e5adSMichael Roth 340c216e5adSMichael Roth QTAILQ_FOREACH_SAFE(mount, &guest_fsfreeze_state.mount_list, next, temp) { 341c216e5adSMichael Roth QTAILQ_REMOVE(&guest_fsfreeze_state.mount_list, mount, next); 342c216e5adSMichael Roth g_free(mount->dirname); 343c216e5adSMichael Roth g_free(mount->devtype); 344c216e5adSMichael Roth g_free(mount); 345c216e5adSMichael Roth } 346c216e5adSMichael Roth 347c216e5adSMichael Roth fp = setmntent(mtab, "r"); 348c216e5adSMichael Roth if (!fp) { 349c216e5adSMichael Roth g_warning("fsfreeze: unable to read mtab"); 350c216e5adSMichael Roth return -1; 351c216e5adSMichael Roth } 352c216e5adSMichael Roth 353c216e5adSMichael Roth while ((ment = getmntent(fp))) { 354c216e5adSMichael Roth /* 355c216e5adSMichael Roth * An entry which device name doesn't start with a '/' is 356c216e5adSMichael Roth * either a dummy file system or a network file system. 357c216e5adSMichael Roth * Add special handling for smbfs and cifs as is done by 358c216e5adSMichael Roth * coreutils as well. 359c216e5adSMichael Roth */ 360c216e5adSMichael Roth if ((ment->mnt_fsname[0] != '/') || 361c216e5adSMichael Roth (strcmp(ment->mnt_type, "smbfs") == 0) || 362c216e5adSMichael Roth (strcmp(ment->mnt_type, "cifs") == 0)) { 363c216e5adSMichael Roth continue; 364c216e5adSMichael Roth } 365c216e5adSMichael Roth 366c216e5adSMichael Roth mount = g_malloc0(sizeof(GuestFsfreezeMount)); 367c216e5adSMichael Roth mount->dirname = g_strdup(ment->mnt_dir); 368c216e5adSMichael Roth mount->devtype = g_strdup(ment->mnt_type); 369c216e5adSMichael Roth 370c216e5adSMichael Roth QTAILQ_INSERT_TAIL(&guest_fsfreeze_state.mount_list, mount, next); 371c216e5adSMichael Roth } 372c216e5adSMichael Roth 373c216e5adSMichael Roth endmntent(fp); 374c216e5adSMichael Roth 375c216e5adSMichael Roth return 0; 376c216e5adSMichael Roth } 377c216e5adSMichael Roth 378c216e5adSMichael Roth /* 379c216e5adSMichael Roth * Return status of freeze/thaw 380c216e5adSMichael Roth */ 381c216e5adSMichael Roth GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **err) 382c216e5adSMichael Roth { 383c216e5adSMichael Roth return guest_fsfreeze_state.status; 384c216e5adSMichael Roth } 385c216e5adSMichael Roth 386c216e5adSMichael Roth /* 387c216e5adSMichael Roth * Walk list of mounted file systems in the guest, and freeze the ones which 388c216e5adSMichael Roth * are real local file systems. 389c216e5adSMichael Roth */ 390c216e5adSMichael Roth int64_t qmp_guest_fsfreeze_freeze(Error **err) 391c216e5adSMichael Roth { 392c216e5adSMichael Roth int ret = 0, i = 0; 393c216e5adSMichael Roth struct GuestFsfreezeMount *mount, *temp; 394c216e5adSMichael Roth int fd; 395c216e5adSMichael Roth char err_msg[512]; 396c216e5adSMichael Roth 397c216e5adSMichael Roth slog("guest-fsfreeze called"); 398c216e5adSMichael Roth 399c216e5adSMichael Roth if (guest_fsfreeze_state.status == GUEST_FSFREEZE_STATUS_FROZEN) { 400c216e5adSMichael Roth return 0; 401c216e5adSMichael Roth } 402c216e5adSMichael Roth 403c216e5adSMichael Roth ret = guest_fsfreeze_build_mount_list(); 404c216e5adSMichael Roth if (ret < 0) { 405c216e5adSMichael Roth return ret; 406c216e5adSMichael Roth } 407c216e5adSMichael Roth 408c216e5adSMichael Roth /* cannot risk guest agent blocking itself on a write in this state */ 409c216e5adSMichael Roth disable_logging(); 410c216e5adSMichael Roth 411c216e5adSMichael Roth QTAILQ_FOREACH_SAFE(mount, &guest_fsfreeze_state.mount_list, next, temp) { 412c216e5adSMichael Roth fd = qemu_open(mount->dirname, O_RDONLY); 413c216e5adSMichael Roth if (fd == -1) { 414c216e5adSMichael Roth sprintf(err_msg, "failed to open %s, %s", mount->dirname, strerror(errno)); 415c216e5adSMichael Roth error_set(err, QERR_QGA_COMMAND_FAILED, err_msg); 416c216e5adSMichael Roth goto error; 417c216e5adSMichael Roth } 418c216e5adSMichael Roth 419c216e5adSMichael Roth /* we try to cull filesytems we know won't work in advance, but other 420c216e5adSMichael Roth * filesytems may not implement fsfreeze for less obvious reasons. 421c216e5adSMichael Roth * these will report EOPNOTSUPP, so we simply ignore them. when 422c216e5adSMichael Roth * thawing, these filesystems will return an EINVAL instead, due to 423c216e5adSMichael Roth * not being in a frozen state. Other filesystem-specific 424c216e5adSMichael Roth * errors may result in EINVAL, however, so the user should check the 425c216e5adSMichael Roth * number * of filesystems returned here against those returned by the 426c216e5adSMichael Roth * thaw operation to determine whether everything completed 427c216e5adSMichael Roth * successfully 428c216e5adSMichael Roth */ 429c216e5adSMichael Roth ret = ioctl(fd, FIFREEZE); 430c216e5adSMichael Roth if (ret < 0 && errno != EOPNOTSUPP) { 431c216e5adSMichael Roth sprintf(err_msg, "failed to freeze %s, %s", mount->dirname, strerror(errno)); 432c216e5adSMichael Roth error_set(err, QERR_QGA_COMMAND_FAILED, err_msg); 433c216e5adSMichael Roth close(fd); 434c216e5adSMichael Roth goto error; 435c216e5adSMichael Roth } 436c216e5adSMichael Roth close(fd); 437c216e5adSMichael Roth 438c216e5adSMichael Roth i++; 439c216e5adSMichael Roth } 440c216e5adSMichael Roth 441c216e5adSMichael Roth guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_FROZEN; 442c216e5adSMichael Roth return i; 443c216e5adSMichael Roth 444c216e5adSMichael Roth error: 445c216e5adSMichael Roth if (i > 0) { 446c216e5adSMichael Roth qmp_guest_fsfreeze_thaw(NULL); 447c216e5adSMichael Roth } 448c216e5adSMichael Roth return 0; 449c216e5adSMichael Roth } 450c216e5adSMichael Roth 451c216e5adSMichael Roth /* 452c216e5adSMichael Roth * Walk list of frozen file systems in the guest, and thaw them. 453c216e5adSMichael Roth */ 454c216e5adSMichael Roth int64_t qmp_guest_fsfreeze_thaw(Error **err) 455c216e5adSMichael Roth { 456c216e5adSMichael Roth int ret; 457c216e5adSMichael Roth GuestFsfreezeMount *mount, *temp; 458c216e5adSMichael Roth int fd, i = 0; 459c216e5adSMichael Roth bool has_error = false; 460c216e5adSMichael Roth 461c216e5adSMichael Roth QTAILQ_FOREACH_SAFE(mount, &guest_fsfreeze_state.mount_list, next, temp) { 462c216e5adSMichael Roth fd = qemu_open(mount->dirname, O_RDONLY); 463c216e5adSMichael Roth if (fd == -1) { 464c216e5adSMichael Roth has_error = true; 465c216e5adSMichael Roth continue; 466c216e5adSMichael Roth } 467c216e5adSMichael Roth ret = ioctl(fd, FITHAW); 468c216e5adSMichael Roth if (ret < 0 && errno != EOPNOTSUPP && errno != EINVAL) { 469c216e5adSMichael Roth has_error = true; 470c216e5adSMichael Roth close(fd); 471c216e5adSMichael Roth continue; 472c216e5adSMichael Roth } 473c216e5adSMichael Roth close(fd); 474c216e5adSMichael Roth i++; 475c216e5adSMichael Roth } 476c216e5adSMichael Roth 477c216e5adSMichael Roth if (has_error) { 478c216e5adSMichael Roth guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_ERROR; 479c216e5adSMichael Roth } else { 480c216e5adSMichael Roth guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_THAWED; 481c216e5adSMichael Roth } 482c216e5adSMichael Roth enable_logging(); 483c216e5adSMichael Roth return i; 484c216e5adSMichael Roth } 485c216e5adSMichael Roth 486c216e5adSMichael Roth static void guest_fsfreeze_init(void) 487c216e5adSMichael Roth { 488c216e5adSMichael Roth guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_THAWED; 489c216e5adSMichael Roth QTAILQ_INIT(&guest_fsfreeze_state.mount_list); 490c216e5adSMichael Roth } 491c216e5adSMichael Roth 492c216e5adSMichael Roth static void guest_fsfreeze_cleanup(void) 493c216e5adSMichael Roth { 494c216e5adSMichael Roth int64_t ret; 495c216e5adSMichael Roth Error *err = NULL; 496c216e5adSMichael Roth 497c216e5adSMichael Roth if (guest_fsfreeze_state.status == GUEST_FSFREEZE_STATUS_FROZEN) { 498c216e5adSMichael Roth ret = qmp_guest_fsfreeze_thaw(&err); 499c216e5adSMichael Roth if (ret < 0 || err) { 500c216e5adSMichael Roth slog("failed to clean up frozen filesystems"); 501c216e5adSMichael Roth } 502c216e5adSMichael Roth } 503c216e5adSMichael Roth } 504c216e5adSMichael Roth #else 505c216e5adSMichael Roth /* 506c216e5adSMichael Roth * Return status of freeze/thaw 507c216e5adSMichael Roth */ 508c216e5adSMichael Roth GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **err) 509c216e5adSMichael Roth { 510c216e5adSMichael Roth error_set(err, QERR_UNSUPPORTED); 511c216e5adSMichael Roth 512c216e5adSMichael Roth return 0; 513c216e5adSMichael Roth } 514c216e5adSMichael Roth 515c216e5adSMichael Roth /* 516c216e5adSMichael Roth * Walk list of mounted file systems in the guest, and freeze the ones which 517c216e5adSMichael Roth * are real local file systems. 518c216e5adSMichael Roth */ 519c216e5adSMichael Roth int64_t qmp_guest_fsfreeze_freeze(Error **err) 520c216e5adSMichael Roth { 521c216e5adSMichael Roth error_set(err, QERR_UNSUPPORTED); 522c216e5adSMichael Roth 523c216e5adSMichael Roth return 0; 524c216e5adSMichael Roth } 525c216e5adSMichael Roth 526c216e5adSMichael Roth /* 527c216e5adSMichael Roth * Walk list of frozen file systems in the guest, and thaw them. 528c216e5adSMichael Roth */ 529c216e5adSMichael Roth int64_t qmp_guest_fsfreeze_thaw(Error **err) 530c216e5adSMichael Roth { 531c216e5adSMichael Roth error_set(err, QERR_UNSUPPORTED); 532c216e5adSMichael Roth 533c216e5adSMichael Roth return 0; 534c216e5adSMichael Roth } 535c216e5adSMichael Roth #endif 536c216e5adSMichael Roth 53711d0f125SLuiz Capitulino #define LINUX_SYS_STATE_FILE "/sys/power/state" 53811d0f125SLuiz Capitulino #define SUSPEND_SUPPORTED 0 53911d0f125SLuiz Capitulino #define SUSPEND_NOT_SUPPORTED 1 54011d0f125SLuiz Capitulino 54111d0f125SLuiz Capitulino /** 54211d0f125SLuiz Capitulino * This function forks twice and the information about the mode support 54311d0f125SLuiz Capitulino * status is passed to the qemu-ga process via a pipe. 54411d0f125SLuiz Capitulino * 54511d0f125SLuiz Capitulino * This approach allows us to keep the way we reap terminated children 54611d0f125SLuiz Capitulino * in qemu-ga quite simple. 54711d0f125SLuiz Capitulino */ 54811d0f125SLuiz Capitulino static void bios_supports_mode(const char *pmutils_bin, const char *pmutils_arg, 54911d0f125SLuiz Capitulino const char *sysfile_str, Error **err) 55011d0f125SLuiz Capitulino { 55111d0f125SLuiz Capitulino pid_t pid; 55211d0f125SLuiz Capitulino ssize_t ret; 55311d0f125SLuiz Capitulino char *pmutils_path; 55411d0f125SLuiz Capitulino int status, pipefds[2]; 55511d0f125SLuiz Capitulino 55611d0f125SLuiz Capitulino if (pipe(pipefds) < 0) { 55711d0f125SLuiz Capitulino error_set(err, QERR_UNDEFINED_ERROR); 55811d0f125SLuiz Capitulino return; 55911d0f125SLuiz Capitulino } 56011d0f125SLuiz Capitulino 56111d0f125SLuiz Capitulino pmutils_path = g_find_program_in_path(pmutils_bin); 56211d0f125SLuiz Capitulino 56311d0f125SLuiz Capitulino pid = fork(); 56411d0f125SLuiz Capitulino if (!pid) { 56511d0f125SLuiz Capitulino struct sigaction act; 56611d0f125SLuiz Capitulino 56711d0f125SLuiz Capitulino memset(&act, 0, sizeof(act)); 56811d0f125SLuiz Capitulino act.sa_handler = SIG_DFL; 56911d0f125SLuiz Capitulino sigaction(SIGCHLD, &act, NULL); 57011d0f125SLuiz Capitulino 57111d0f125SLuiz Capitulino setsid(); 57211d0f125SLuiz Capitulino close(pipefds[0]); 57311d0f125SLuiz Capitulino reopen_fd_to_null(0); 57411d0f125SLuiz Capitulino reopen_fd_to_null(1); 57511d0f125SLuiz Capitulino reopen_fd_to_null(2); 57611d0f125SLuiz Capitulino 57711d0f125SLuiz Capitulino pid = fork(); 57811d0f125SLuiz Capitulino if (!pid) { 57911d0f125SLuiz Capitulino int fd; 58011d0f125SLuiz Capitulino char buf[32]; /* hopefully big enough */ 58111d0f125SLuiz Capitulino 58211d0f125SLuiz Capitulino if (pmutils_path) { 58311d0f125SLuiz Capitulino execle(pmutils_path, pmutils_bin, pmutils_arg, NULL, environ); 58411d0f125SLuiz Capitulino } 58511d0f125SLuiz Capitulino 58611d0f125SLuiz Capitulino /* 58711d0f125SLuiz Capitulino * If we get here either pm-utils is not installed or execle() has 58811d0f125SLuiz Capitulino * failed. Let's try the manual method if the caller wants it. 58911d0f125SLuiz Capitulino */ 59011d0f125SLuiz Capitulino 59111d0f125SLuiz Capitulino if (!sysfile_str) { 59211d0f125SLuiz Capitulino _exit(SUSPEND_NOT_SUPPORTED); 59311d0f125SLuiz Capitulino } 59411d0f125SLuiz Capitulino 59511d0f125SLuiz Capitulino fd = open(LINUX_SYS_STATE_FILE, O_RDONLY); 59611d0f125SLuiz Capitulino if (fd < 0) { 59711d0f125SLuiz Capitulino _exit(SUSPEND_NOT_SUPPORTED); 59811d0f125SLuiz Capitulino } 59911d0f125SLuiz Capitulino 60011d0f125SLuiz Capitulino ret = read(fd, buf, sizeof(buf)-1); 60111d0f125SLuiz Capitulino if (ret <= 0) { 60211d0f125SLuiz Capitulino _exit(SUSPEND_NOT_SUPPORTED); 60311d0f125SLuiz Capitulino } 60411d0f125SLuiz Capitulino buf[ret] = '\0'; 60511d0f125SLuiz Capitulino 60611d0f125SLuiz Capitulino if (strstr(buf, sysfile_str)) { 60711d0f125SLuiz Capitulino _exit(SUSPEND_SUPPORTED); 60811d0f125SLuiz Capitulino } 60911d0f125SLuiz Capitulino 61011d0f125SLuiz Capitulino _exit(SUSPEND_NOT_SUPPORTED); 61111d0f125SLuiz Capitulino } 61211d0f125SLuiz Capitulino 61311d0f125SLuiz Capitulino if (pid > 0) { 61411d0f125SLuiz Capitulino wait(&status); 61511d0f125SLuiz Capitulino } else { 61611d0f125SLuiz Capitulino status = SUSPEND_NOT_SUPPORTED; 61711d0f125SLuiz Capitulino } 61811d0f125SLuiz Capitulino 61911d0f125SLuiz Capitulino ret = write(pipefds[1], &status, sizeof(status)); 62011d0f125SLuiz Capitulino if (ret != sizeof(status)) { 62111d0f125SLuiz Capitulino _exit(EXIT_FAILURE); 62211d0f125SLuiz Capitulino } 62311d0f125SLuiz Capitulino 62411d0f125SLuiz Capitulino _exit(EXIT_SUCCESS); 62511d0f125SLuiz Capitulino } 62611d0f125SLuiz Capitulino 62711d0f125SLuiz Capitulino close(pipefds[1]); 62811d0f125SLuiz Capitulino g_free(pmutils_path); 62911d0f125SLuiz Capitulino 63011d0f125SLuiz Capitulino if (pid < 0) { 63111d0f125SLuiz Capitulino error_set(err, QERR_UNDEFINED_ERROR); 63211d0f125SLuiz Capitulino goto out; 63311d0f125SLuiz Capitulino } 63411d0f125SLuiz Capitulino 63511d0f125SLuiz Capitulino ret = read(pipefds[0], &status, sizeof(status)); 63611d0f125SLuiz Capitulino if (ret == sizeof(status) && WIFEXITED(status) && 63711d0f125SLuiz Capitulino WEXITSTATUS(status) == SUSPEND_SUPPORTED) { 63811d0f125SLuiz Capitulino goto out; 63911d0f125SLuiz Capitulino } 64011d0f125SLuiz Capitulino 64111d0f125SLuiz Capitulino error_set(err, QERR_UNSUPPORTED); 64211d0f125SLuiz Capitulino 64311d0f125SLuiz Capitulino out: 64411d0f125SLuiz Capitulino close(pipefds[0]); 64511d0f125SLuiz Capitulino } 64611d0f125SLuiz Capitulino 64711d0f125SLuiz Capitulino static void guest_suspend(const char *pmutils_bin, const char *sysfile_str, 64811d0f125SLuiz Capitulino Error **err) 64911d0f125SLuiz Capitulino { 65011d0f125SLuiz Capitulino pid_t pid; 65111d0f125SLuiz Capitulino char *pmutils_path; 65211d0f125SLuiz Capitulino 65311d0f125SLuiz Capitulino pmutils_path = g_find_program_in_path(pmutils_bin); 65411d0f125SLuiz Capitulino 65511d0f125SLuiz Capitulino pid = fork(); 65611d0f125SLuiz Capitulino if (pid == 0) { 65711d0f125SLuiz Capitulino /* child */ 65811d0f125SLuiz Capitulino int fd; 65911d0f125SLuiz Capitulino 66011d0f125SLuiz Capitulino setsid(); 66111d0f125SLuiz Capitulino reopen_fd_to_null(0); 66211d0f125SLuiz Capitulino reopen_fd_to_null(1); 66311d0f125SLuiz Capitulino reopen_fd_to_null(2); 66411d0f125SLuiz Capitulino 66511d0f125SLuiz Capitulino if (pmutils_path) { 66611d0f125SLuiz Capitulino execle(pmutils_path, pmutils_bin, NULL, environ); 66711d0f125SLuiz Capitulino } 66811d0f125SLuiz Capitulino 66911d0f125SLuiz Capitulino /* 67011d0f125SLuiz Capitulino * If we get here either pm-utils is not installed or execle() has 67111d0f125SLuiz Capitulino * failed. Let's try the manual method if the caller wants it. 67211d0f125SLuiz Capitulino */ 67311d0f125SLuiz Capitulino 67411d0f125SLuiz Capitulino if (!sysfile_str) { 67511d0f125SLuiz Capitulino _exit(EXIT_FAILURE); 67611d0f125SLuiz Capitulino } 67711d0f125SLuiz Capitulino 67811d0f125SLuiz Capitulino fd = open(LINUX_SYS_STATE_FILE, O_WRONLY); 67911d0f125SLuiz Capitulino if (fd < 0) { 68011d0f125SLuiz Capitulino _exit(EXIT_FAILURE); 68111d0f125SLuiz Capitulino } 68211d0f125SLuiz Capitulino 68311d0f125SLuiz Capitulino if (write(fd, sysfile_str, strlen(sysfile_str)) < 0) { 68411d0f125SLuiz Capitulino _exit(EXIT_FAILURE); 68511d0f125SLuiz Capitulino } 68611d0f125SLuiz Capitulino 68711d0f125SLuiz Capitulino _exit(EXIT_SUCCESS); 68811d0f125SLuiz Capitulino } 68911d0f125SLuiz Capitulino 69011d0f125SLuiz Capitulino g_free(pmutils_path); 69111d0f125SLuiz Capitulino 69211d0f125SLuiz Capitulino if (pid < 0) { 69311d0f125SLuiz Capitulino error_set(err, QERR_UNDEFINED_ERROR); 69411d0f125SLuiz Capitulino return; 69511d0f125SLuiz Capitulino } 69611d0f125SLuiz Capitulino } 69711d0f125SLuiz Capitulino 69811d0f125SLuiz Capitulino void qmp_guest_suspend_disk(Error **err) 69911d0f125SLuiz Capitulino { 70011d0f125SLuiz Capitulino bios_supports_mode("pm-is-supported", "--hibernate", "disk", err); 70111d0f125SLuiz Capitulino if (error_is_set(err)) { 70211d0f125SLuiz Capitulino return; 70311d0f125SLuiz Capitulino } 70411d0f125SLuiz Capitulino 70511d0f125SLuiz Capitulino guest_suspend("pm-hibernate", "disk", err); 70611d0f125SLuiz Capitulino } 70711d0f125SLuiz Capitulino 708fbf42210SLuiz Capitulino void qmp_guest_suspend_ram(Error **err) 709fbf42210SLuiz Capitulino { 710fbf42210SLuiz Capitulino bios_supports_mode("pm-is-supported", "--suspend", "mem", err); 711fbf42210SLuiz Capitulino if (error_is_set(err)) { 712fbf42210SLuiz Capitulino return; 713fbf42210SLuiz Capitulino } 714fbf42210SLuiz Capitulino 715fbf42210SLuiz Capitulino guest_suspend("pm-suspend", "mem", err); 716fbf42210SLuiz Capitulino } 717fbf42210SLuiz Capitulino 718*95f4f404SLuiz Capitulino void qmp_guest_suspend_hybrid(Error **err) 719*95f4f404SLuiz Capitulino { 720*95f4f404SLuiz Capitulino bios_supports_mode("pm-is-supported", "--suspend-hybrid", NULL, err); 721*95f4f404SLuiz Capitulino if (error_is_set(err)) { 722*95f4f404SLuiz Capitulino return; 723*95f4f404SLuiz Capitulino } 724*95f4f404SLuiz Capitulino 725*95f4f404SLuiz Capitulino guest_suspend("pm-suspend-hybrid", NULL, err); 726*95f4f404SLuiz Capitulino } 727*95f4f404SLuiz Capitulino 728c216e5adSMichael Roth /* register init/cleanup routines for stateful command groups */ 729c216e5adSMichael Roth void ga_command_state_init(GAState *s, GACommandState *cs) 730c216e5adSMichael Roth { 731c216e5adSMichael Roth ga_state = s; 732c216e5adSMichael Roth #if defined(CONFIG_FSFREEZE) 733c216e5adSMichael Roth ga_command_state_add(cs, guest_fsfreeze_init, guest_fsfreeze_cleanup); 734c216e5adSMichael Roth #endif 735c216e5adSMichael Roth ga_command_state_add(cs, guest_file_init, NULL); 736c216e5adSMichael Roth } 737