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> 83424fc9fSMichal Privoznik * Michal Privoznik <mprivozn@redhat.com> 9c216e5adSMichael Roth * 10c216e5adSMichael Roth * This work is licensed under the terms of the GNU GPL, version 2 or later. 11c216e5adSMichael Roth * See the COPYING file in the top-level directory. 12c216e5adSMichael Roth */ 13c216e5adSMichael Roth 14c216e5adSMichael Roth #include <glib.h> 15c216e5adSMichael Roth #include <sys/types.h> 16c216e5adSMichael Roth #include <sys/ioctl.h> 172c02cbf6SLuiz Capitulino #include <sys/wait.h> 18d2baff62SLaszlo Ersek #include <unistd.h> 19d2baff62SLaszlo Ersek #include <errno.h> 20d2baff62SLaszlo Ersek #include <fcntl.h> 21d2baff62SLaszlo Ersek #include <inttypes.h> 22c216e5adSMichael Roth #include "qga/guest-agent-core.h" 23c216e5adSMichael Roth #include "qga-qmp-commands.h" 247b1b5d19SPaolo Bonzini #include "qapi/qmp/qerror.h" 251de7afc9SPaolo Bonzini #include "qemu/queue.h" 261de7afc9SPaolo Bonzini #include "qemu/host-utils.h" 27c216e5adSMichael Roth 282c02cbf6SLuiz Capitulino #ifndef CONFIG_HAS_ENVIRON 29eecae147SAndreas Färber #ifdef __APPLE__ 30eecae147SAndreas Färber #include <crt_externs.h> 31eecae147SAndreas Färber #define environ (*_NSGetEnviron()) 32eecae147SAndreas Färber #else 332c02cbf6SLuiz Capitulino extern char **environ; 342c02cbf6SLuiz Capitulino #endif 35eecae147SAndreas Färber #endif 362c02cbf6SLuiz Capitulino 37e72c3f2eSMichael Roth #if defined(__linux__) 38e72c3f2eSMichael Roth #include <mntent.h> 39e72c3f2eSMichael Roth #include <linux/fs.h> 40e72c3f2eSMichael Roth #include <ifaddrs.h> 41e72c3f2eSMichael Roth #include <arpa/inet.h> 42e72c3f2eSMichael Roth #include <sys/socket.h> 43e72c3f2eSMichael Roth #include <net/if.h> 44e72c3f2eSMichael Roth 45eab5fd59SPaolo Bonzini #ifdef FIFREEZE 46e72c3f2eSMichael Roth #define CONFIG_FSFREEZE 47e72c3f2eSMichael Roth #endif 48eab5fd59SPaolo Bonzini #ifdef FITRIM 49eab5fd59SPaolo Bonzini #define CONFIG_FSTRIM 50eab5fd59SPaolo Bonzini #endif 51e72c3f2eSMichael Roth #endif 52e72c3f2eSMichael Roth 53d220a6dfSLuiz Capitulino static void ga_wait_child(pid_t pid, int *status, Error **err) 54d220a6dfSLuiz Capitulino { 55d220a6dfSLuiz Capitulino pid_t rpid; 56d220a6dfSLuiz Capitulino 57d220a6dfSLuiz Capitulino *status = 0; 58d220a6dfSLuiz Capitulino 59d220a6dfSLuiz Capitulino do { 60d220a6dfSLuiz Capitulino rpid = waitpid(pid, status, 0); 61d220a6dfSLuiz Capitulino } while (rpid == -1 && errno == EINTR); 62d220a6dfSLuiz Capitulino 63d220a6dfSLuiz Capitulino if (rpid == -1) { 64d220a6dfSLuiz Capitulino error_setg_errno(err, errno, "failed to wait for child (pid: %d)", pid); 65d220a6dfSLuiz Capitulino return; 66d220a6dfSLuiz Capitulino } 67d220a6dfSLuiz Capitulino 68d220a6dfSLuiz Capitulino g_assert(rpid == pid); 69d220a6dfSLuiz Capitulino } 70d220a6dfSLuiz Capitulino 71c216e5adSMichael Roth void qmp_guest_shutdown(bool has_mode, const char *mode, Error **err) 72c216e5adSMichael Roth { 73c216e5adSMichael Roth const char *shutdown_flag; 74d220a6dfSLuiz Capitulino Error *local_err = NULL; 75d220a6dfSLuiz Capitulino pid_t pid; 763674838cSLuiz Capitulino int status; 77c216e5adSMichael Roth 78c216e5adSMichael Roth slog("guest-shutdown called, mode: %s", mode); 79c216e5adSMichael Roth if (!has_mode || strcmp(mode, "powerdown") == 0) { 80c216e5adSMichael Roth shutdown_flag = "-P"; 81c216e5adSMichael Roth } else if (strcmp(mode, "halt") == 0) { 82c216e5adSMichael Roth shutdown_flag = "-H"; 83c216e5adSMichael Roth } else if (strcmp(mode, "reboot") == 0) { 84c216e5adSMichael Roth shutdown_flag = "-r"; 85c216e5adSMichael Roth } else { 86d220a6dfSLuiz Capitulino error_setg(err, 87d220a6dfSLuiz Capitulino "mode is invalid (valid values are: halt|powerdown|reboot"); 88c216e5adSMichael Roth return; 89c216e5adSMichael Roth } 90c216e5adSMichael Roth 91d5dd3498SLuiz Capitulino pid = fork(); 92d5dd3498SLuiz Capitulino if (pid == 0) { 93c216e5adSMichael Roth /* child, start the shutdown */ 94c216e5adSMichael Roth setsid(); 953674838cSLuiz Capitulino reopen_fd_to_null(0); 963674838cSLuiz Capitulino reopen_fd_to_null(1); 973674838cSLuiz Capitulino reopen_fd_to_null(2); 98c216e5adSMichael Roth 993674838cSLuiz Capitulino execle("/sbin/shutdown", "shutdown", shutdown_flag, "+0", 1003674838cSLuiz Capitulino "hypervisor initiated shutdown", (char*)NULL, environ); 1013674838cSLuiz Capitulino _exit(EXIT_FAILURE); 102d5dd3498SLuiz Capitulino } else if (pid < 0) { 103d220a6dfSLuiz Capitulino error_setg_errno(err, errno, "failed to create child process"); 104d5dd3498SLuiz Capitulino return; 105d5dd3498SLuiz Capitulino } 106d5dd3498SLuiz Capitulino 107d220a6dfSLuiz Capitulino ga_wait_child(pid, &status, &local_err); 108d220a6dfSLuiz Capitulino if (error_is_set(&local_err)) { 109d220a6dfSLuiz Capitulino error_propagate(err, local_err); 110d220a6dfSLuiz Capitulino return; 111d220a6dfSLuiz Capitulino } 112d220a6dfSLuiz Capitulino 113d220a6dfSLuiz Capitulino if (!WIFEXITED(status)) { 114d220a6dfSLuiz Capitulino error_setg(err, "child process has terminated abnormally"); 115d220a6dfSLuiz Capitulino return; 116d220a6dfSLuiz Capitulino } 117d220a6dfSLuiz Capitulino 118d220a6dfSLuiz Capitulino if (WEXITSTATUS(status)) { 119d220a6dfSLuiz Capitulino error_setg(err, "child process has failed to shutdown"); 120d220a6dfSLuiz Capitulino return; 121d220a6dfSLuiz Capitulino } 122d220a6dfSLuiz Capitulino 123d220a6dfSLuiz Capitulino /* succeded */ 124c216e5adSMichael Roth } 125c216e5adSMichael Roth 1266912e6a9SLei Li int64_t qmp_guest_get_time(Error **errp) 1276912e6a9SLei Li { 1286912e6a9SLei Li int ret; 1296912e6a9SLei Li qemu_timeval tq; 1306912e6a9SLei Li int64_t time_ns; 1316912e6a9SLei Li 1326912e6a9SLei Li ret = qemu_gettimeofday(&tq); 1336912e6a9SLei Li if (ret < 0) { 1346912e6a9SLei Li error_setg_errno(errp, errno, "Failed to get time"); 1356912e6a9SLei Li return -1; 1366912e6a9SLei Li } 1376912e6a9SLei Li 1386912e6a9SLei Li time_ns = tq.tv_sec * 1000000000LL + tq.tv_usec * 1000; 1396912e6a9SLei Li return time_ns; 1406912e6a9SLei Li } 1416912e6a9SLei Li 142a1bca57fSLei Li void qmp_guest_set_time(int64_t time_ns, Error **errp) 143a1bca57fSLei Li { 144a1bca57fSLei Li int ret; 145a1bca57fSLei Li int status; 146a1bca57fSLei Li pid_t pid; 147a1bca57fSLei Li Error *local_err = NULL; 148a1bca57fSLei Li struct timeval tv; 149a1bca57fSLei Li 150a1bca57fSLei Li /* year-2038 will overflow in case time_t is 32bit */ 151a1bca57fSLei Li if (time_ns / 1000000000 != (time_t)(time_ns / 1000000000)) { 152a1bca57fSLei Li error_setg(errp, "Time %" PRId64 " is too large", time_ns); 153a1bca57fSLei Li return; 154a1bca57fSLei Li } 155a1bca57fSLei Li 156a1bca57fSLei Li tv.tv_sec = time_ns / 1000000000; 157a1bca57fSLei Li tv.tv_usec = (time_ns % 1000000000) / 1000; 158a1bca57fSLei Li 159a1bca57fSLei Li ret = settimeofday(&tv, NULL); 160a1bca57fSLei Li if (ret < 0) { 161a1bca57fSLei Li error_setg_errno(errp, errno, "Failed to set time to guest"); 162a1bca57fSLei Li return; 163a1bca57fSLei Li } 164a1bca57fSLei Li 165a1bca57fSLei Li /* Set the Hardware Clock to the current System Time. */ 166a1bca57fSLei Li pid = fork(); 167a1bca57fSLei Li if (pid == 0) { 168a1bca57fSLei Li setsid(); 169a1bca57fSLei Li reopen_fd_to_null(0); 170a1bca57fSLei Li reopen_fd_to_null(1); 171a1bca57fSLei Li reopen_fd_to_null(2); 172a1bca57fSLei Li 173a1bca57fSLei Li execle("/sbin/hwclock", "hwclock", "-w", NULL, environ); 174a1bca57fSLei Li _exit(EXIT_FAILURE); 175a1bca57fSLei Li } else if (pid < 0) { 176a1bca57fSLei Li error_setg_errno(errp, errno, "failed to create child process"); 177a1bca57fSLei Li return; 178a1bca57fSLei Li } 179a1bca57fSLei Li 180a1bca57fSLei Li ga_wait_child(pid, &status, &local_err); 181a1bca57fSLei Li if (error_is_set(&local_err)) { 182a1bca57fSLei Li error_propagate(errp, local_err); 183a1bca57fSLei Li return; 184a1bca57fSLei Li } 185a1bca57fSLei Li 186a1bca57fSLei Li if (!WIFEXITED(status)) { 187a1bca57fSLei Li error_setg(errp, "child process has terminated abnormally"); 188a1bca57fSLei Li return; 189a1bca57fSLei Li } 190a1bca57fSLei Li 191a1bca57fSLei Li if (WEXITSTATUS(status)) { 192a1bca57fSLei Li error_setg(errp, "hwclock failed to set hardware clock to system time"); 193a1bca57fSLei Li return; 194a1bca57fSLei Li } 195a1bca57fSLei Li } 196a1bca57fSLei Li 197c216e5adSMichael Roth typedef struct GuestFileHandle { 198c216e5adSMichael Roth uint64_t id; 199c216e5adSMichael Roth FILE *fh; 200c216e5adSMichael Roth QTAILQ_ENTRY(GuestFileHandle) next; 201c216e5adSMichael Roth } GuestFileHandle; 202c216e5adSMichael Roth 203c216e5adSMichael Roth static struct { 204c216e5adSMichael Roth QTAILQ_HEAD(, GuestFileHandle) filehandles; 205c216e5adSMichael Roth } guest_file_state; 206c216e5adSMichael Roth 20739097dafSMichael Roth static int64_t guest_file_handle_add(FILE *fh, Error **errp) 208c216e5adSMichael Roth { 209c216e5adSMichael Roth GuestFileHandle *gfh; 21039097dafSMichael Roth int64_t handle; 21139097dafSMichael Roth 21239097dafSMichael Roth handle = ga_get_fd_handle(ga_state, errp); 21339097dafSMichael Roth if (error_is_set(errp)) { 21439097dafSMichael Roth return 0; 21539097dafSMichael Roth } 216c216e5adSMichael Roth 217c216e5adSMichael Roth gfh = g_malloc0(sizeof(GuestFileHandle)); 21839097dafSMichael Roth gfh->id = handle; 219c216e5adSMichael Roth gfh->fh = fh; 220c216e5adSMichael Roth QTAILQ_INSERT_TAIL(&guest_file_state.filehandles, gfh, next); 22139097dafSMichael Roth 22239097dafSMichael Roth return handle; 223c216e5adSMichael Roth } 224c216e5adSMichael Roth 225a9de6d01SLuiz Capitulino static GuestFileHandle *guest_file_handle_find(int64_t id, Error **err) 226c216e5adSMichael Roth { 227c216e5adSMichael Roth GuestFileHandle *gfh; 228c216e5adSMichael Roth 229c216e5adSMichael Roth QTAILQ_FOREACH(gfh, &guest_file_state.filehandles, next) 230c216e5adSMichael Roth { 231c216e5adSMichael Roth if (gfh->id == id) { 232c216e5adSMichael Roth return gfh; 233c216e5adSMichael Roth } 234c216e5adSMichael Roth } 235c216e5adSMichael Roth 236a9de6d01SLuiz Capitulino error_setg(err, "handle '%" PRId64 "' has not been found", id); 237c216e5adSMichael Roth return NULL; 238c216e5adSMichael Roth } 239c216e5adSMichael Roth 240c216e5adSMichael Roth int64_t qmp_guest_file_open(const char *path, bool has_mode, const char *mode, Error **err) 241c216e5adSMichael Roth { 242c216e5adSMichael Roth FILE *fh; 243c216e5adSMichael Roth int fd; 24439097dafSMichael Roth int64_t ret = -1, handle; 245c216e5adSMichael Roth 246c216e5adSMichael Roth if (!has_mode) { 247c216e5adSMichael Roth mode = "r"; 248c216e5adSMichael Roth } 249c216e5adSMichael Roth slog("guest-file-open called, filepath: %s, mode: %s", path, mode); 250c216e5adSMichael Roth fh = fopen(path, mode); 251c216e5adSMichael Roth if (!fh) { 252db3edb66SLuiz Capitulino error_setg_errno(err, errno, "failed to open file '%s' (mode: '%s')", 253db3edb66SLuiz Capitulino path, mode); 254c216e5adSMichael Roth return -1; 255c216e5adSMichael Roth } 256c216e5adSMichael Roth 257c216e5adSMichael Roth /* set fd non-blocking to avoid common use cases (like reading from a 258c216e5adSMichael Roth * named pipe) from hanging the agent 259c216e5adSMichael Roth */ 260c216e5adSMichael Roth fd = fileno(fh); 261c216e5adSMichael Roth ret = fcntl(fd, F_GETFL); 262c216e5adSMichael Roth ret = fcntl(fd, F_SETFL, ret | O_NONBLOCK); 263c216e5adSMichael Roth if (ret == -1) { 264db3edb66SLuiz Capitulino error_setg_errno(err, errno, "failed to make file '%s' non-blocking", 265db3edb66SLuiz Capitulino path); 266c216e5adSMichael Roth fclose(fh); 267c216e5adSMichael Roth return -1; 268c216e5adSMichael Roth } 269c216e5adSMichael Roth 27039097dafSMichael Roth handle = guest_file_handle_add(fh, err); 27139097dafSMichael Roth if (error_is_set(err)) { 27239097dafSMichael Roth fclose(fh); 27339097dafSMichael Roth return -1; 27439097dafSMichael Roth } 27539097dafSMichael Roth 27639097dafSMichael Roth slog("guest-file-open, handle: %d", handle); 27739097dafSMichael Roth return handle; 278c216e5adSMichael Roth } 279c216e5adSMichael Roth 280c216e5adSMichael Roth void qmp_guest_file_close(int64_t handle, Error **err) 281c216e5adSMichael Roth { 282a9de6d01SLuiz Capitulino GuestFileHandle *gfh = guest_file_handle_find(handle, err); 283c216e5adSMichael Roth int ret; 284c216e5adSMichael Roth 285c216e5adSMichael Roth slog("guest-file-close called, handle: %ld", handle); 286c216e5adSMichael Roth if (!gfh) { 287c216e5adSMichael Roth return; 288c216e5adSMichael Roth } 289c216e5adSMichael Roth 290c216e5adSMichael Roth ret = fclose(gfh->fh); 2913ac4b7c5SLuiz Capitulino if (ret == EOF) { 292db3edb66SLuiz Capitulino error_setg_errno(err, errno, "failed to close handle"); 293c216e5adSMichael Roth return; 294c216e5adSMichael Roth } 295c216e5adSMichael Roth 296c216e5adSMichael Roth QTAILQ_REMOVE(&guest_file_state.filehandles, gfh, next); 297c216e5adSMichael Roth g_free(gfh); 298c216e5adSMichael Roth } 299c216e5adSMichael Roth 300c216e5adSMichael Roth struct GuestFileRead *qmp_guest_file_read(int64_t handle, bool has_count, 301c216e5adSMichael Roth int64_t count, Error **err) 302c216e5adSMichael Roth { 303a9de6d01SLuiz Capitulino GuestFileHandle *gfh = guest_file_handle_find(handle, err); 304c216e5adSMichael Roth GuestFileRead *read_data = NULL; 305c216e5adSMichael Roth guchar *buf; 306c216e5adSMichael Roth FILE *fh; 307c216e5adSMichael Roth size_t read_count; 308c216e5adSMichael Roth 309c216e5adSMichael Roth if (!gfh) { 310c216e5adSMichael Roth return NULL; 311c216e5adSMichael Roth } 312c216e5adSMichael Roth 313c216e5adSMichael Roth if (!has_count) { 314c216e5adSMichael Roth count = QGA_READ_COUNT_DEFAULT; 315c216e5adSMichael Roth } else if (count < 0) { 316db3edb66SLuiz Capitulino error_setg(err, "value '%" PRId64 "' is invalid for argument count", 317db3edb66SLuiz Capitulino count); 318c216e5adSMichael Roth return NULL; 319c216e5adSMichael Roth } 320c216e5adSMichael Roth 321c216e5adSMichael Roth fh = gfh->fh; 322c216e5adSMichael Roth buf = g_malloc0(count+1); 323c216e5adSMichael Roth read_count = fread(buf, 1, count, fh); 324c216e5adSMichael Roth if (ferror(fh)) { 325db3edb66SLuiz Capitulino error_setg_errno(err, errno, "failed to read file"); 326c216e5adSMichael Roth slog("guest-file-read failed, handle: %ld", handle); 327c216e5adSMichael Roth } else { 328c216e5adSMichael Roth buf[read_count] = 0; 329c216e5adSMichael Roth read_data = g_malloc0(sizeof(GuestFileRead)); 330c216e5adSMichael Roth read_data->count = read_count; 331c216e5adSMichael Roth read_data->eof = feof(fh); 332c216e5adSMichael Roth if (read_count) { 333c216e5adSMichael Roth read_data->buf_b64 = g_base64_encode(buf, read_count); 334c216e5adSMichael Roth } 335c216e5adSMichael Roth } 336c216e5adSMichael Roth g_free(buf); 337c216e5adSMichael Roth clearerr(fh); 338c216e5adSMichael Roth 339c216e5adSMichael Roth return read_data; 340c216e5adSMichael Roth } 341c216e5adSMichael Roth 342c216e5adSMichael Roth GuestFileWrite *qmp_guest_file_write(int64_t handle, const char *buf_b64, 343c216e5adSMichael Roth bool has_count, int64_t count, Error **err) 344c216e5adSMichael Roth { 345c216e5adSMichael Roth GuestFileWrite *write_data = NULL; 346c216e5adSMichael Roth guchar *buf; 347c216e5adSMichael Roth gsize buf_len; 348c216e5adSMichael Roth int write_count; 349a9de6d01SLuiz Capitulino GuestFileHandle *gfh = guest_file_handle_find(handle, err); 350c216e5adSMichael Roth FILE *fh; 351c216e5adSMichael Roth 352c216e5adSMichael Roth if (!gfh) { 353c216e5adSMichael Roth return NULL; 354c216e5adSMichael Roth } 355c216e5adSMichael Roth 356c216e5adSMichael Roth fh = gfh->fh; 357c216e5adSMichael Roth buf = g_base64_decode(buf_b64, &buf_len); 358c216e5adSMichael Roth 359c216e5adSMichael Roth if (!has_count) { 360c216e5adSMichael Roth count = buf_len; 361c216e5adSMichael Roth } else if (count < 0 || count > buf_len) { 362db3edb66SLuiz Capitulino error_setg(err, "value '%" PRId64 "' is invalid for argument count", 363db3edb66SLuiz Capitulino count); 364c216e5adSMichael Roth g_free(buf); 365c216e5adSMichael Roth return NULL; 366c216e5adSMichael Roth } 367c216e5adSMichael Roth 368c216e5adSMichael Roth write_count = fwrite(buf, 1, count, fh); 369c216e5adSMichael Roth if (ferror(fh)) { 370db3edb66SLuiz Capitulino error_setg_errno(err, errno, "failed to write to file"); 371c216e5adSMichael Roth slog("guest-file-write failed, handle: %ld", handle); 372c216e5adSMichael Roth } else { 373c216e5adSMichael Roth write_data = g_malloc0(sizeof(GuestFileWrite)); 374c216e5adSMichael Roth write_data->count = write_count; 375c216e5adSMichael Roth write_data->eof = feof(fh); 376c216e5adSMichael Roth } 377c216e5adSMichael Roth g_free(buf); 378c216e5adSMichael Roth clearerr(fh); 379c216e5adSMichael Roth 380c216e5adSMichael Roth return write_data; 381c216e5adSMichael Roth } 382c216e5adSMichael Roth 383c216e5adSMichael Roth struct GuestFileSeek *qmp_guest_file_seek(int64_t handle, int64_t offset, 384c216e5adSMichael Roth int64_t whence, Error **err) 385c216e5adSMichael Roth { 386a9de6d01SLuiz Capitulino GuestFileHandle *gfh = guest_file_handle_find(handle, err); 387c216e5adSMichael Roth GuestFileSeek *seek_data = NULL; 388c216e5adSMichael Roth FILE *fh; 389c216e5adSMichael Roth int ret; 390c216e5adSMichael Roth 391c216e5adSMichael Roth if (!gfh) { 392c216e5adSMichael Roth return NULL; 393c216e5adSMichael Roth } 394c216e5adSMichael Roth 395c216e5adSMichael Roth fh = gfh->fh; 396c216e5adSMichael Roth ret = fseek(fh, offset, whence); 397c216e5adSMichael Roth if (ret == -1) { 398db3edb66SLuiz Capitulino error_setg_errno(err, errno, "failed to seek file"); 399c216e5adSMichael Roth } else { 400c216e5adSMichael Roth seek_data = g_malloc0(sizeof(GuestFileRead)); 401c216e5adSMichael Roth seek_data->position = ftell(fh); 402c216e5adSMichael Roth seek_data->eof = feof(fh); 403c216e5adSMichael Roth } 404c216e5adSMichael Roth clearerr(fh); 405c216e5adSMichael Roth 406c216e5adSMichael Roth return seek_data; 407c216e5adSMichael Roth } 408c216e5adSMichael Roth 409c216e5adSMichael Roth void qmp_guest_file_flush(int64_t handle, Error **err) 410c216e5adSMichael Roth { 411a9de6d01SLuiz Capitulino GuestFileHandle *gfh = guest_file_handle_find(handle, err); 412c216e5adSMichael Roth FILE *fh; 413c216e5adSMichael Roth int ret; 414c216e5adSMichael Roth 415c216e5adSMichael Roth if (!gfh) { 416c216e5adSMichael Roth return; 417c216e5adSMichael Roth } 418c216e5adSMichael Roth 419c216e5adSMichael Roth fh = gfh->fh; 420c216e5adSMichael Roth ret = fflush(fh); 421c216e5adSMichael Roth if (ret == EOF) { 422db3edb66SLuiz Capitulino error_setg_errno(err, errno, "failed to flush file"); 423c216e5adSMichael Roth } 424c216e5adSMichael Roth } 425c216e5adSMichael Roth 426c216e5adSMichael Roth static void guest_file_init(void) 427c216e5adSMichael Roth { 428c216e5adSMichael Roth QTAILQ_INIT(&guest_file_state.filehandles); 429c216e5adSMichael Roth } 430c216e5adSMichael Roth 431e72c3f2eSMichael Roth /* linux-specific implementations. avoid this if at all possible. */ 432e72c3f2eSMichael Roth #if defined(__linux__) 433e72c3f2eSMichael Roth 434eab5fd59SPaolo Bonzini #if defined(CONFIG_FSFREEZE) || defined(CONFIG_FSTRIM) 435af02203fSPaolo Bonzini typedef struct FsMount { 436c216e5adSMichael Roth char *dirname; 437c216e5adSMichael Roth char *devtype; 438af02203fSPaolo Bonzini QTAILQ_ENTRY(FsMount) next; 439af02203fSPaolo Bonzini } FsMount; 440c216e5adSMichael Roth 441af02203fSPaolo Bonzini typedef QTAILQ_HEAD(, FsMount) FsMountList; 4429e8aded4SMichael Roth 443af02203fSPaolo Bonzini static void free_fs_mount_list(FsMountList *mounts) 444c216e5adSMichael Roth { 445af02203fSPaolo Bonzini FsMount *mount, *temp; 446c216e5adSMichael Roth 4479e8aded4SMichael Roth if (!mounts) { 4489e8aded4SMichael Roth return; 4499e8aded4SMichael Roth } 4509e8aded4SMichael Roth 4519e8aded4SMichael Roth QTAILQ_FOREACH_SAFE(mount, mounts, next, temp) { 4529e8aded4SMichael Roth QTAILQ_REMOVE(mounts, mount, next); 453c216e5adSMichael Roth g_free(mount->dirname); 454c216e5adSMichael Roth g_free(mount->devtype); 455c216e5adSMichael Roth g_free(mount); 456c216e5adSMichael Roth } 4579e8aded4SMichael Roth } 4589e8aded4SMichael Roth 4599e8aded4SMichael Roth /* 4609e8aded4SMichael Roth * Walk the mount table and build a list of local file systems 4619e8aded4SMichael Roth */ 462261551d1SLuiz Capitulino static void build_fs_mount_list(FsMountList *mounts, Error **err) 4639e8aded4SMichael Roth { 4649e8aded4SMichael Roth struct mntent *ment; 465af02203fSPaolo Bonzini FsMount *mount; 4669e2fa418SMichael Roth char const *mtab = "/proc/self/mounts"; 4679e8aded4SMichael Roth FILE *fp; 468c216e5adSMichael Roth 469c216e5adSMichael Roth fp = setmntent(mtab, "r"); 470c216e5adSMichael Roth if (!fp) { 471261551d1SLuiz Capitulino error_setg(err, "failed to open mtab file: '%s'", mtab); 472261551d1SLuiz Capitulino return; 473c216e5adSMichael Roth } 474c216e5adSMichael Roth 475c216e5adSMichael Roth while ((ment = getmntent(fp))) { 476c216e5adSMichael Roth /* 477c216e5adSMichael Roth * An entry which device name doesn't start with a '/' is 478c216e5adSMichael Roth * either a dummy file system or a network file system. 479c216e5adSMichael Roth * Add special handling for smbfs and cifs as is done by 480c216e5adSMichael Roth * coreutils as well. 481c216e5adSMichael Roth */ 482c216e5adSMichael Roth if ((ment->mnt_fsname[0] != '/') || 483c216e5adSMichael Roth (strcmp(ment->mnt_type, "smbfs") == 0) || 484c216e5adSMichael Roth (strcmp(ment->mnt_type, "cifs") == 0)) { 485c216e5adSMichael Roth continue; 486c216e5adSMichael Roth } 487c216e5adSMichael Roth 488af02203fSPaolo Bonzini mount = g_malloc0(sizeof(FsMount)); 489c216e5adSMichael Roth mount->dirname = g_strdup(ment->mnt_dir); 490c216e5adSMichael Roth mount->devtype = g_strdup(ment->mnt_type); 491c216e5adSMichael Roth 4929e8aded4SMichael Roth QTAILQ_INSERT_TAIL(mounts, mount, next); 493c216e5adSMichael Roth } 494c216e5adSMichael Roth 495c216e5adSMichael Roth endmntent(fp); 496c216e5adSMichael Roth } 497eab5fd59SPaolo Bonzini #endif 498eab5fd59SPaolo Bonzini 499eab5fd59SPaolo Bonzini #if defined(CONFIG_FSFREEZE) 500c216e5adSMichael Roth 501ec0f694cSTomoki Sekiyama typedef enum { 502ec0f694cSTomoki Sekiyama FSFREEZE_HOOK_THAW = 0, 503ec0f694cSTomoki Sekiyama FSFREEZE_HOOK_FREEZE, 504ec0f694cSTomoki Sekiyama } FsfreezeHookArg; 505ec0f694cSTomoki Sekiyama 506ec0f694cSTomoki Sekiyama const char *fsfreeze_hook_arg_string[] = { 507ec0f694cSTomoki Sekiyama "thaw", 508ec0f694cSTomoki Sekiyama "freeze", 509ec0f694cSTomoki Sekiyama }; 510ec0f694cSTomoki Sekiyama 511ec0f694cSTomoki Sekiyama static void execute_fsfreeze_hook(FsfreezeHookArg arg, Error **err) 512ec0f694cSTomoki Sekiyama { 513ec0f694cSTomoki Sekiyama int status; 514ec0f694cSTomoki Sekiyama pid_t pid; 515ec0f694cSTomoki Sekiyama const char *hook; 516ec0f694cSTomoki Sekiyama const char *arg_str = fsfreeze_hook_arg_string[arg]; 517ec0f694cSTomoki Sekiyama Error *local_err = NULL; 518ec0f694cSTomoki Sekiyama 519ec0f694cSTomoki Sekiyama hook = ga_fsfreeze_hook(ga_state); 520ec0f694cSTomoki Sekiyama if (!hook) { 521ec0f694cSTomoki Sekiyama return; 522ec0f694cSTomoki Sekiyama } 523ec0f694cSTomoki Sekiyama if (access(hook, X_OK) != 0) { 524ec0f694cSTomoki Sekiyama error_setg_errno(err, errno, "can't access fsfreeze hook '%s'", hook); 525ec0f694cSTomoki Sekiyama return; 526ec0f694cSTomoki Sekiyama } 527ec0f694cSTomoki Sekiyama 528ec0f694cSTomoki Sekiyama slog("executing fsfreeze hook with arg '%s'", arg_str); 529ec0f694cSTomoki Sekiyama pid = fork(); 530ec0f694cSTomoki Sekiyama if (pid == 0) { 531ec0f694cSTomoki Sekiyama setsid(); 532ec0f694cSTomoki Sekiyama reopen_fd_to_null(0); 533ec0f694cSTomoki Sekiyama reopen_fd_to_null(1); 534ec0f694cSTomoki Sekiyama reopen_fd_to_null(2); 535ec0f694cSTomoki Sekiyama 536ec0f694cSTomoki Sekiyama execle(hook, hook, arg_str, NULL, environ); 537ec0f694cSTomoki Sekiyama _exit(EXIT_FAILURE); 538ec0f694cSTomoki Sekiyama } else if (pid < 0) { 539ec0f694cSTomoki Sekiyama error_setg_errno(err, errno, "failed to create child process"); 540ec0f694cSTomoki Sekiyama return; 541ec0f694cSTomoki Sekiyama } 542ec0f694cSTomoki Sekiyama 543ec0f694cSTomoki Sekiyama ga_wait_child(pid, &status, &local_err); 544ec0f694cSTomoki Sekiyama if (error_is_set(&local_err)) { 545ec0f694cSTomoki Sekiyama error_propagate(err, local_err); 546ec0f694cSTomoki Sekiyama return; 547ec0f694cSTomoki Sekiyama } 548ec0f694cSTomoki Sekiyama 549ec0f694cSTomoki Sekiyama if (!WIFEXITED(status)) { 550ec0f694cSTomoki Sekiyama error_setg(err, "fsfreeze hook has terminated abnormally"); 551ec0f694cSTomoki Sekiyama return; 552ec0f694cSTomoki Sekiyama } 553ec0f694cSTomoki Sekiyama 554ec0f694cSTomoki Sekiyama status = WEXITSTATUS(status); 555ec0f694cSTomoki Sekiyama if (status) { 556ec0f694cSTomoki Sekiyama error_setg(err, "fsfreeze hook has failed with status %d", status); 557ec0f694cSTomoki Sekiyama return; 558ec0f694cSTomoki Sekiyama } 559ec0f694cSTomoki Sekiyama } 560ec0f694cSTomoki Sekiyama 561c216e5adSMichael Roth /* 562c216e5adSMichael Roth * Return status of freeze/thaw 563c216e5adSMichael Roth */ 564c216e5adSMichael Roth GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **err) 565c216e5adSMichael Roth { 566f22d85e9SMichael Roth if (ga_is_frozen(ga_state)) { 567f22d85e9SMichael Roth return GUEST_FSFREEZE_STATUS_FROZEN; 568f22d85e9SMichael Roth } 569f22d85e9SMichael Roth 570f22d85e9SMichael Roth return GUEST_FSFREEZE_STATUS_THAWED; 571c216e5adSMichael Roth } 572c216e5adSMichael Roth 573c216e5adSMichael Roth /* 574c216e5adSMichael Roth * Walk list of mounted file systems in the guest, and freeze the ones which 575c216e5adSMichael Roth * are real local file systems. 576c216e5adSMichael Roth */ 577c216e5adSMichael Roth int64_t qmp_guest_fsfreeze_freeze(Error **err) 578c216e5adSMichael Roth { 579c216e5adSMichael Roth int ret = 0, i = 0; 580af02203fSPaolo Bonzini FsMountList mounts; 581af02203fSPaolo Bonzini struct FsMount *mount; 582261551d1SLuiz Capitulino Error *local_err = NULL; 583c216e5adSMichael Roth int fd; 584c216e5adSMichael Roth 585c216e5adSMichael Roth slog("guest-fsfreeze called"); 586c216e5adSMichael Roth 587ec0f694cSTomoki Sekiyama execute_fsfreeze_hook(FSFREEZE_HOOK_FREEZE, &local_err); 588ec0f694cSTomoki Sekiyama if (error_is_set(&local_err)) { 589ec0f694cSTomoki Sekiyama error_propagate(err, local_err); 590ec0f694cSTomoki Sekiyama return -1; 591ec0f694cSTomoki Sekiyama } 592ec0f694cSTomoki Sekiyama 5939e8aded4SMichael Roth QTAILQ_INIT(&mounts); 594261551d1SLuiz Capitulino build_fs_mount_list(&mounts, &local_err); 595261551d1SLuiz Capitulino if (error_is_set(&local_err)) { 596261551d1SLuiz Capitulino error_propagate(err, local_err); 597261551d1SLuiz Capitulino return -1; 598c216e5adSMichael Roth } 599c216e5adSMichael Roth 600c216e5adSMichael Roth /* cannot risk guest agent blocking itself on a write in this state */ 601f22d85e9SMichael Roth ga_set_frozen(ga_state); 602c216e5adSMichael Roth 6039e8aded4SMichael Roth QTAILQ_FOREACH(mount, &mounts, next) { 604c216e5adSMichael Roth fd = qemu_open(mount->dirname, O_RDONLY); 605c216e5adSMichael Roth if (fd == -1) { 606617fbbc1SLuiz Capitulino error_setg_errno(err, errno, "failed to open %s", mount->dirname); 607c216e5adSMichael Roth goto error; 608c216e5adSMichael Roth } 609c216e5adSMichael Roth 610c216e5adSMichael Roth /* we try to cull filesytems we know won't work in advance, but other 611c216e5adSMichael Roth * filesytems may not implement fsfreeze for less obvious reasons. 6129e8aded4SMichael Roth * these will report EOPNOTSUPP. we simply ignore these when tallying 6139e8aded4SMichael Roth * the number of frozen filesystems. 6149e8aded4SMichael Roth * 6159e8aded4SMichael Roth * any other error means a failure to freeze a filesystem we 6169e8aded4SMichael Roth * expect to be freezable, so return an error in those cases 6179e8aded4SMichael Roth * and return system to thawed state. 618c216e5adSMichael Roth */ 619c216e5adSMichael Roth ret = ioctl(fd, FIFREEZE); 6209e8aded4SMichael Roth if (ret == -1) { 6219e8aded4SMichael Roth if (errno != EOPNOTSUPP) { 622617fbbc1SLuiz Capitulino error_setg_errno(err, errno, "failed to freeze %s", 623617fbbc1SLuiz Capitulino mount->dirname); 624c216e5adSMichael Roth close(fd); 625c216e5adSMichael Roth goto error; 626c216e5adSMichael Roth } 6279e8aded4SMichael Roth } else { 628c216e5adSMichael Roth i++; 629c216e5adSMichael Roth } 6309e8aded4SMichael Roth close(fd); 6319e8aded4SMichael Roth } 632c216e5adSMichael Roth 633af02203fSPaolo Bonzini free_fs_mount_list(&mounts); 634c216e5adSMichael Roth return i; 635c216e5adSMichael Roth 636c216e5adSMichael Roth error: 637af02203fSPaolo Bonzini free_fs_mount_list(&mounts); 638c216e5adSMichael Roth qmp_guest_fsfreeze_thaw(NULL); 639c216e5adSMichael Roth return 0; 640c216e5adSMichael Roth } 641c216e5adSMichael Roth 642c216e5adSMichael Roth /* 643c216e5adSMichael Roth * Walk list of frozen file systems in the guest, and thaw them. 644c216e5adSMichael Roth */ 645c216e5adSMichael Roth int64_t qmp_guest_fsfreeze_thaw(Error **err) 646c216e5adSMichael Roth { 647c216e5adSMichael Roth int ret; 648af02203fSPaolo Bonzini FsMountList mounts; 649af02203fSPaolo Bonzini FsMount *mount; 6509e8aded4SMichael Roth int fd, i = 0, logged; 651261551d1SLuiz Capitulino Error *local_err = NULL; 652c216e5adSMichael Roth 6539e8aded4SMichael Roth QTAILQ_INIT(&mounts); 654261551d1SLuiz Capitulino build_fs_mount_list(&mounts, &local_err); 655261551d1SLuiz Capitulino if (error_is_set(&local_err)) { 656261551d1SLuiz Capitulino error_propagate(err, local_err); 6579e8aded4SMichael Roth return 0; 6589e8aded4SMichael Roth } 6599e8aded4SMichael Roth 6609e8aded4SMichael Roth QTAILQ_FOREACH(mount, &mounts, next) { 6619e8aded4SMichael Roth logged = false; 662c216e5adSMichael Roth fd = qemu_open(mount->dirname, O_RDONLY); 663c216e5adSMichael Roth if (fd == -1) { 664c216e5adSMichael Roth continue; 665c216e5adSMichael Roth } 6669e8aded4SMichael Roth /* we have no way of knowing whether a filesystem was actually unfrozen 6679e8aded4SMichael Roth * as a result of a successful call to FITHAW, only that if an error 6689e8aded4SMichael Roth * was returned the filesystem was *not* unfrozen by that particular 6699e8aded4SMichael Roth * call. 6709e8aded4SMichael Roth * 671a31f0531SJim Meyering * since multiple preceding FIFREEZEs require multiple calls to FITHAW 6729e8aded4SMichael Roth * to unfreeze, continuing issuing FITHAW until an error is returned, 6739e8aded4SMichael Roth * in which case either the filesystem is in an unfreezable state, or, 6749e8aded4SMichael Roth * more likely, it was thawed previously (and remains so afterward). 6759e8aded4SMichael Roth * 6769e8aded4SMichael Roth * also, since the most recent successful call is the one that did 6779e8aded4SMichael Roth * the actual unfreeze, we can use this to provide an accurate count 6789e8aded4SMichael Roth * of the number of filesystems unfrozen by guest-fsfreeze-thaw, which 6799e8aded4SMichael Roth * may * be useful for determining whether a filesystem was unfrozen 6809e8aded4SMichael Roth * during the freeze/thaw phase by a process other than qemu-ga. 6819e8aded4SMichael Roth */ 6829e8aded4SMichael Roth do { 683c216e5adSMichael Roth ret = ioctl(fd, FITHAW); 6849e8aded4SMichael Roth if (ret == 0 && !logged) { 685c216e5adSMichael Roth i++; 6869e8aded4SMichael Roth logged = true; 6879e8aded4SMichael Roth } 6889e8aded4SMichael Roth } while (ret == 0); 6899e8aded4SMichael Roth close(fd); 690c216e5adSMichael Roth } 691c216e5adSMichael Roth 692f22d85e9SMichael Roth ga_unset_frozen(ga_state); 693af02203fSPaolo Bonzini free_fs_mount_list(&mounts); 694ec0f694cSTomoki Sekiyama 695ec0f694cSTomoki Sekiyama execute_fsfreeze_hook(FSFREEZE_HOOK_THAW, err); 696ec0f694cSTomoki Sekiyama 697c216e5adSMichael Roth return i; 698c216e5adSMichael Roth } 699c216e5adSMichael Roth 700c216e5adSMichael Roth static void guest_fsfreeze_cleanup(void) 701c216e5adSMichael Roth { 702c216e5adSMichael Roth Error *err = NULL; 703c216e5adSMichael Roth 704f22d85e9SMichael Roth if (ga_is_frozen(ga_state) == GUEST_FSFREEZE_STATUS_FROZEN) { 7056f686749SMarkus Armbruster qmp_guest_fsfreeze_thaw(&err); 7066f686749SMarkus Armbruster if (err) { 7076f686749SMarkus Armbruster slog("failed to clean up frozen filesystems: %s", 7086f686749SMarkus Armbruster error_get_pretty(err)); 7096f686749SMarkus Armbruster error_free(err); 710c216e5adSMichael Roth } 711c216e5adSMichael Roth } 712c216e5adSMichael Roth } 713e72c3f2eSMichael Roth #endif /* CONFIG_FSFREEZE */ 714c216e5adSMichael Roth 715eab5fd59SPaolo Bonzini #if defined(CONFIG_FSTRIM) 716eab5fd59SPaolo Bonzini /* 717eab5fd59SPaolo Bonzini * Walk list of mounted file systems in the guest, and trim them. 718eab5fd59SPaolo Bonzini */ 719eab5fd59SPaolo Bonzini void qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **err) 720eab5fd59SPaolo Bonzini { 721eab5fd59SPaolo Bonzini int ret = 0; 722eab5fd59SPaolo Bonzini FsMountList mounts; 723eab5fd59SPaolo Bonzini struct FsMount *mount; 724eab5fd59SPaolo Bonzini int fd; 725261551d1SLuiz Capitulino Error *local_err = NULL; 726eab5fd59SPaolo Bonzini struct fstrim_range r = { 727eab5fd59SPaolo Bonzini .start = 0, 728eab5fd59SPaolo Bonzini .len = -1, 729eab5fd59SPaolo Bonzini .minlen = has_minimum ? minimum : 0, 730eab5fd59SPaolo Bonzini }; 731eab5fd59SPaolo Bonzini 732eab5fd59SPaolo Bonzini slog("guest-fstrim called"); 733eab5fd59SPaolo Bonzini 734eab5fd59SPaolo Bonzini QTAILQ_INIT(&mounts); 735261551d1SLuiz Capitulino build_fs_mount_list(&mounts, &local_err); 736261551d1SLuiz Capitulino if (error_is_set(&local_err)) { 737261551d1SLuiz Capitulino error_propagate(err, local_err); 738eab5fd59SPaolo Bonzini return; 739eab5fd59SPaolo Bonzini } 740eab5fd59SPaolo Bonzini 741eab5fd59SPaolo Bonzini QTAILQ_FOREACH(mount, &mounts, next) { 742eab5fd59SPaolo Bonzini fd = qemu_open(mount->dirname, O_RDONLY); 743eab5fd59SPaolo Bonzini if (fd == -1) { 744071673b0SLuiz Capitulino error_setg_errno(err, errno, "failed to open %s", mount->dirname); 745eab5fd59SPaolo Bonzini goto error; 746eab5fd59SPaolo Bonzini } 747eab5fd59SPaolo Bonzini 748eab5fd59SPaolo Bonzini /* We try to cull filesytems we know won't work in advance, but other 749eab5fd59SPaolo Bonzini * filesytems may not implement fstrim for less obvious reasons. These 750eab5fd59SPaolo Bonzini * will report EOPNOTSUPP; we simply ignore these errors. Any other 751eab5fd59SPaolo Bonzini * error means an unexpected error, so return it in those cases. In 752eab5fd59SPaolo Bonzini * some other cases ENOTTY will be reported (e.g. CD-ROMs). 753eab5fd59SPaolo Bonzini */ 754eab5fd59SPaolo Bonzini ret = ioctl(fd, FITRIM, &r); 755eab5fd59SPaolo Bonzini if (ret == -1) { 756eab5fd59SPaolo Bonzini if (errno != ENOTTY && errno != EOPNOTSUPP) { 757071673b0SLuiz Capitulino error_setg_errno(err, errno, "failed to trim %s", 758071673b0SLuiz Capitulino mount->dirname); 759eab5fd59SPaolo Bonzini close(fd); 760eab5fd59SPaolo Bonzini goto error; 761eab5fd59SPaolo Bonzini } 762eab5fd59SPaolo Bonzini } 763eab5fd59SPaolo Bonzini close(fd); 764eab5fd59SPaolo Bonzini } 765eab5fd59SPaolo Bonzini 766eab5fd59SPaolo Bonzini error: 767eab5fd59SPaolo Bonzini free_fs_mount_list(&mounts); 768eab5fd59SPaolo Bonzini } 769eab5fd59SPaolo Bonzini #endif /* CONFIG_FSTRIM */ 770eab5fd59SPaolo Bonzini 771eab5fd59SPaolo Bonzini 77211d0f125SLuiz Capitulino #define LINUX_SYS_STATE_FILE "/sys/power/state" 77311d0f125SLuiz Capitulino #define SUSPEND_SUPPORTED 0 77411d0f125SLuiz Capitulino #define SUSPEND_NOT_SUPPORTED 1 77511d0f125SLuiz Capitulino 77611d0f125SLuiz Capitulino static void bios_supports_mode(const char *pmutils_bin, const char *pmutils_arg, 77711d0f125SLuiz Capitulino const char *sysfile_str, Error **err) 77811d0f125SLuiz Capitulino { 7796b26e837SLuiz Capitulino Error *local_err = NULL; 78011d0f125SLuiz Capitulino char *pmutils_path; 7816b26e837SLuiz Capitulino pid_t pid; 782dc8764f0SLuiz Capitulino int status; 78311d0f125SLuiz Capitulino 78411d0f125SLuiz Capitulino pmutils_path = g_find_program_in_path(pmutils_bin); 78511d0f125SLuiz Capitulino 78611d0f125SLuiz Capitulino pid = fork(); 78711d0f125SLuiz Capitulino if (!pid) { 788dc8764f0SLuiz Capitulino char buf[32]; /* hopefully big enough */ 789dc8764f0SLuiz Capitulino ssize_t ret; 790dc8764f0SLuiz Capitulino int fd; 79111d0f125SLuiz Capitulino 79211d0f125SLuiz Capitulino setsid(); 79311d0f125SLuiz Capitulino reopen_fd_to_null(0); 79411d0f125SLuiz Capitulino reopen_fd_to_null(1); 79511d0f125SLuiz Capitulino reopen_fd_to_null(2); 79611d0f125SLuiz Capitulino 79711d0f125SLuiz Capitulino if (pmutils_path) { 79811d0f125SLuiz Capitulino execle(pmutils_path, pmutils_bin, pmutils_arg, NULL, environ); 79911d0f125SLuiz Capitulino } 80011d0f125SLuiz Capitulino 80111d0f125SLuiz Capitulino /* 80211d0f125SLuiz Capitulino * If we get here either pm-utils is not installed or execle() has 80311d0f125SLuiz Capitulino * failed. Let's try the manual method if the caller wants it. 80411d0f125SLuiz Capitulino */ 80511d0f125SLuiz Capitulino 80611d0f125SLuiz Capitulino if (!sysfile_str) { 80711d0f125SLuiz Capitulino _exit(SUSPEND_NOT_SUPPORTED); 80811d0f125SLuiz Capitulino } 80911d0f125SLuiz Capitulino 81011d0f125SLuiz Capitulino fd = open(LINUX_SYS_STATE_FILE, O_RDONLY); 81111d0f125SLuiz Capitulino if (fd < 0) { 81211d0f125SLuiz Capitulino _exit(SUSPEND_NOT_SUPPORTED); 81311d0f125SLuiz Capitulino } 81411d0f125SLuiz Capitulino 81511d0f125SLuiz Capitulino ret = read(fd, buf, sizeof(buf)-1); 81611d0f125SLuiz Capitulino if (ret <= 0) { 81711d0f125SLuiz Capitulino _exit(SUSPEND_NOT_SUPPORTED); 81811d0f125SLuiz Capitulino } 81911d0f125SLuiz Capitulino buf[ret] = '\0'; 82011d0f125SLuiz Capitulino 82111d0f125SLuiz Capitulino if (strstr(buf, sysfile_str)) { 82211d0f125SLuiz Capitulino _exit(SUSPEND_SUPPORTED); 82311d0f125SLuiz Capitulino } 82411d0f125SLuiz Capitulino 82511d0f125SLuiz Capitulino _exit(SUSPEND_NOT_SUPPORTED); 8266b26e837SLuiz Capitulino } else if (pid < 0) { 8276b26e837SLuiz Capitulino error_setg_errno(err, errno, "failed to create child process"); 8286b26e837SLuiz Capitulino goto out; 82911d0f125SLuiz Capitulino } 83011d0f125SLuiz Capitulino 8316b26e837SLuiz Capitulino ga_wait_child(pid, &status, &local_err); 8326b26e837SLuiz Capitulino if (error_is_set(&local_err)) { 8336b26e837SLuiz Capitulino error_propagate(err, local_err); 8346b26e837SLuiz Capitulino goto out; 83511d0f125SLuiz Capitulino } 83611d0f125SLuiz Capitulino 8376b26e837SLuiz Capitulino if (!WIFEXITED(status)) { 8386b26e837SLuiz Capitulino error_setg(err, "child process has terminated abnormally"); 8396b26e837SLuiz Capitulino goto out; 8406b26e837SLuiz Capitulino } 8416b26e837SLuiz Capitulino 842dc8764f0SLuiz Capitulino switch (WEXITSTATUS(status)) { 843dc8764f0SLuiz Capitulino case SUSPEND_SUPPORTED: 8446b26e837SLuiz Capitulino goto out; 845dc8764f0SLuiz Capitulino case SUSPEND_NOT_SUPPORTED: 8466b26e837SLuiz Capitulino error_setg(err, 8476b26e837SLuiz Capitulino "the requested suspend mode is not supported by the guest"); 8486b26e837SLuiz Capitulino goto out; 849dc8764f0SLuiz Capitulino default: 8506b26e837SLuiz Capitulino error_setg(err, 8516b26e837SLuiz Capitulino "the helper program '%s' returned an unexpected exit status" 8526b26e837SLuiz Capitulino " code (%d)", pmutils_path, WEXITSTATUS(status)); 8536b26e837SLuiz Capitulino goto out; 854dc8764f0SLuiz Capitulino } 85511d0f125SLuiz Capitulino 8566b26e837SLuiz Capitulino out: 8576b26e837SLuiz Capitulino g_free(pmutils_path); 85811d0f125SLuiz Capitulino } 85911d0f125SLuiz Capitulino 86011d0f125SLuiz Capitulino static void guest_suspend(const char *pmutils_bin, const char *sysfile_str, 86111d0f125SLuiz Capitulino Error **err) 86211d0f125SLuiz Capitulino { 8637b376087SLuiz Capitulino Error *local_err = NULL; 86411d0f125SLuiz Capitulino char *pmutils_path; 8657b376087SLuiz Capitulino pid_t pid; 866dc8764f0SLuiz Capitulino int status; 86711d0f125SLuiz Capitulino 86811d0f125SLuiz Capitulino pmutils_path = g_find_program_in_path(pmutils_bin); 86911d0f125SLuiz Capitulino 87011d0f125SLuiz Capitulino pid = fork(); 87111d0f125SLuiz Capitulino if (pid == 0) { 87211d0f125SLuiz Capitulino /* child */ 87311d0f125SLuiz Capitulino int fd; 87411d0f125SLuiz Capitulino 87511d0f125SLuiz Capitulino setsid(); 87611d0f125SLuiz Capitulino reopen_fd_to_null(0); 87711d0f125SLuiz Capitulino reopen_fd_to_null(1); 87811d0f125SLuiz Capitulino reopen_fd_to_null(2); 87911d0f125SLuiz Capitulino 88011d0f125SLuiz Capitulino if (pmutils_path) { 88111d0f125SLuiz Capitulino execle(pmutils_path, pmutils_bin, NULL, environ); 88211d0f125SLuiz Capitulino } 88311d0f125SLuiz Capitulino 88411d0f125SLuiz Capitulino /* 88511d0f125SLuiz Capitulino * If we get here either pm-utils is not installed or execle() has 88611d0f125SLuiz Capitulino * failed. Let's try the manual method if the caller wants it. 88711d0f125SLuiz Capitulino */ 88811d0f125SLuiz Capitulino 88911d0f125SLuiz Capitulino if (!sysfile_str) { 89011d0f125SLuiz Capitulino _exit(EXIT_FAILURE); 89111d0f125SLuiz Capitulino } 89211d0f125SLuiz Capitulino 89311d0f125SLuiz Capitulino fd = open(LINUX_SYS_STATE_FILE, O_WRONLY); 89411d0f125SLuiz Capitulino if (fd < 0) { 89511d0f125SLuiz Capitulino _exit(EXIT_FAILURE); 89611d0f125SLuiz Capitulino } 89711d0f125SLuiz Capitulino 89811d0f125SLuiz Capitulino if (write(fd, sysfile_str, strlen(sysfile_str)) < 0) { 89911d0f125SLuiz Capitulino _exit(EXIT_FAILURE); 90011d0f125SLuiz Capitulino } 90111d0f125SLuiz Capitulino 90211d0f125SLuiz Capitulino _exit(EXIT_SUCCESS); 9037b376087SLuiz Capitulino } else if (pid < 0) { 9047b376087SLuiz Capitulino error_setg_errno(err, errno, "failed to create child process"); 9057b376087SLuiz Capitulino goto out; 90611d0f125SLuiz Capitulino } 90711d0f125SLuiz Capitulino 9087b376087SLuiz Capitulino ga_wait_child(pid, &status, &local_err); 9097b376087SLuiz Capitulino if (error_is_set(&local_err)) { 9107b376087SLuiz Capitulino error_propagate(err, local_err); 9117b376087SLuiz Capitulino goto out; 9127b376087SLuiz Capitulino } 9137b376087SLuiz Capitulino 9147b376087SLuiz Capitulino if (!WIFEXITED(status)) { 9157b376087SLuiz Capitulino error_setg(err, "child process has terminated abnormally"); 9167b376087SLuiz Capitulino goto out; 9177b376087SLuiz Capitulino } 9187b376087SLuiz Capitulino 9197b376087SLuiz Capitulino if (WEXITSTATUS(status)) { 9207b376087SLuiz Capitulino error_setg(err, "child process has failed to suspend"); 9217b376087SLuiz Capitulino goto out; 9227b376087SLuiz Capitulino } 9237b376087SLuiz Capitulino 9247b376087SLuiz Capitulino out: 92511d0f125SLuiz Capitulino g_free(pmutils_path); 92611d0f125SLuiz Capitulino } 92711d0f125SLuiz Capitulino 92811d0f125SLuiz Capitulino void qmp_guest_suspend_disk(Error **err) 92911d0f125SLuiz Capitulino { 93011d0f125SLuiz Capitulino bios_supports_mode("pm-is-supported", "--hibernate", "disk", err); 93111d0f125SLuiz Capitulino if (error_is_set(err)) { 93211d0f125SLuiz Capitulino return; 93311d0f125SLuiz Capitulino } 93411d0f125SLuiz Capitulino 93511d0f125SLuiz Capitulino guest_suspend("pm-hibernate", "disk", err); 93611d0f125SLuiz Capitulino } 93711d0f125SLuiz Capitulino 938fbf42210SLuiz Capitulino void qmp_guest_suspend_ram(Error **err) 939fbf42210SLuiz Capitulino { 940fbf42210SLuiz Capitulino bios_supports_mode("pm-is-supported", "--suspend", "mem", err); 941fbf42210SLuiz Capitulino if (error_is_set(err)) { 942fbf42210SLuiz Capitulino return; 943fbf42210SLuiz Capitulino } 944fbf42210SLuiz Capitulino 945fbf42210SLuiz Capitulino guest_suspend("pm-suspend", "mem", err); 946fbf42210SLuiz Capitulino } 947fbf42210SLuiz Capitulino 94895f4f404SLuiz Capitulino void qmp_guest_suspend_hybrid(Error **err) 94995f4f404SLuiz Capitulino { 95095f4f404SLuiz Capitulino bios_supports_mode("pm-is-supported", "--suspend-hybrid", NULL, err); 95195f4f404SLuiz Capitulino if (error_is_set(err)) { 95295f4f404SLuiz Capitulino return; 95395f4f404SLuiz Capitulino } 95495f4f404SLuiz Capitulino 95595f4f404SLuiz Capitulino guest_suspend("pm-suspend-hybrid", NULL, err); 95695f4f404SLuiz Capitulino } 95795f4f404SLuiz Capitulino 9583424fc9fSMichal Privoznik static GuestNetworkInterfaceList * 9593424fc9fSMichal Privoznik guest_find_interface(GuestNetworkInterfaceList *head, 9603424fc9fSMichal Privoznik const char *name) 9613424fc9fSMichal Privoznik { 9623424fc9fSMichal Privoznik for (; head; head = head->next) { 9633424fc9fSMichal Privoznik if (strcmp(head->value->name, name) == 0) { 9643424fc9fSMichal Privoznik break; 9653424fc9fSMichal Privoznik } 9663424fc9fSMichal Privoznik } 9673424fc9fSMichal Privoznik 9683424fc9fSMichal Privoznik return head; 9693424fc9fSMichal Privoznik } 9703424fc9fSMichal Privoznik 9713424fc9fSMichal Privoznik /* 9723424fc9fSMichal Privoznik * Build information about guest interfaces 9733424fc9fSMichal Privoznik */ 9743424fc9fSMichal Privoznik GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp) 9753424fc9fSMichal Privoznik { 9763424fc9fSMichal Privoznik GuestNetworkInterfaceList *head = NULL, *cur_item = NULL; 9773424fc9fSMichal Privoznik struct ifaddrs *ifap, *ifa; 9783424fc9fSMichal Privoznik 9793424fc9fSMichal Privoznik if (getifaddrs(&ifap) < 0) { 980878a0ae0SLuiz Capitulino error_setg_errno(errp, errno, "getifaddrs failed"); 9813424fc9fSMichal Privoznik goto error; 9823424fc9fSMichal Privoznik } 9833424fc9fSMichal Privoznik 9843424fc9fSMichal Privoznik for (ifa = ifap; ifa; ifa = ifa->ifa_next) { 9853424fc9fSMichal Privoznik GuestNetworkInterfaceList *info; 9863424fc9fSMichal Privoznik GuestIpAddressList **address_list = NULL, *address_item = NULL; 9873424fc9fSMichal Privoznik char addr4[INET_ADDRSTRLEN]; 9883424fc9fSMichal Privoznik char addr6[INET6_ADDRSTRLEN]; 9893424fc9fSMichal Privoznik int sock; 9903424fc9fSMichal Privoznik struct ifreq ifr; 9913424fc9fSMichal Privoznik unsigned char *mac_addr; 9923424fc9fSMichal Privoznik void *p; 9933424fc9fSMichal Privoznik 9943424fc9fSMichal Privoznik g_debug("Processing %s interface", ifa->ifa_name); 9953424fc9fSMichal Privoznik 9963424fc9fSMichal Privoznik info = guest_find_interface(head, ifa->ifa_name); 9973424fc9fSMichal Privoznik 9983424fc9fSMichal Privoznik if (!info) { 9993424fc9fSMichal Privoznik info = g_malloc0(sizeof(*info)); 10003424fc9fSMichal Privoznik info->value = g_malloc0(sizeof(*info->value)); 10013424fc9fSMichal Privoznik info->value->name = g_strdup(ifa->ifa_name); 10023424fc9fSMichal Privoznik 10033424fc9fSMichal Privoznik if (!cur_item) { 10043424fc9fSMichal Privoznik head = cur_item = info; 10053424fc9fSMichal Privoznik } else { 10063424fc9fSMichal Privoznik cur_item->next = info; 10073424fc9fSMichal Privoznik cur_item = info; 10083424fc9fSMichal Privoznik } 10093424fc9fSMichal Privoznik } 10103424fc9fSMichal Privoznik 10113424fc9fSMichal Privoznik if (!info->value->has_hardware_address && 10123424fc9fSMichal Privoznik ifa->ifa_flags & SIOCGIFHWADDR) { 10133424fc9fSMichal Privoznik /* we haven't obtained HW address yet */ 10143424fc9fSMichal Privoznik sock = socket(PF_INET, SOCK_STREAM, 0); 10153424fc9fSMichal Privoznik if (sock == -1) { 1016878a0ae0SLuiz Capitulino error_setg_errno(errp, errno, "failed to create socket"); 10173424fc9fSMichal Privoznik goto error; 10183424fc9fSMichal Privoznik } 10193424fc9fSMichal Privoznik 10203424fc9fSMichal Privoznik memset(&ifr, 0, sizeof(ifr)); 10211ab516edSJim Meyering pstrcpy(ifr.ifr_name, IF_NAMESIZE, info->value->name); 10223424fc9fSMichal Privoznik if (ioctl(sock, SIOCGIFHWADDR, &ifr) == -1) { 1023878a0ae0SLuiz Capitulino error_setg_errno(errp, errno, 1024878a0ae0SLuiz Capitulino "failed to get MAC address of %s", 1025878a0ae0SLuiz Capitulino ifa->ifa_name); 102610a2158fSMarkus Armbruster close(sock); 10273424fc9fSMichal Privoznik goto error; 10283424fc9fSMichal Privoznik } 10293424fc9fSMichal Privoznik 103010a2158fSMarkus Armbruster close(sock); 10313424fc9fSMichal Privoznik mac_addr = (unsigned char *) &ifr.ifr_hwaddr.sa_data; 10323424fc9fSMichal Privoznik 1033e4ada482SStefan Weil info->value->hardware_address = 1034e4ada482SStefan Weil g_strdup_printf("%02x:%02x:%02x:%02x:%02x:%02x", 10353424fc9fSMichal Privoznik (int) mac_addr[0], (int) mac_addr[1], 10363424fc9fSMichal Privoznik (int) mac_addr[2], (int) mac_addr[3], 1037e4ada482SStefan Weil (int) mac_addr[4], (int) mac_addr[5]); 10383424fc9fSMichal Privoznik 10393424fc9fSMichal Privoznik info->value->has_hardware_address = true; 10403424fc9fSMichal Privoznik } 10413424fc9fSMichal Privoznik 10423424fc9fSMichal Privoznik if (ifa->ifa_addr && 10433424fc9fSMichal Privoznik ifa->ifa_addr->sa_family == AF_INET) { 10443424fc9fSMichal Privoznik /* interface with IPv4 address */ 10453424fc9fSMichal Privoznik p = &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr; 10463424fc9fSMichal Privoznik if (!inet_ntop(AF_INET, p, addr4, sizeof(addr4))) { 1047878a0ae0SLuiz Capitulino error_setg_errno(errp, errno, "inet_ntop failed"); 10483424fc9fSMichal Privoznik goto error; 10493424fc9fSMichal Privoznik } 10503424fc9fSMichal Privoznik 105110a2158fSMarkus Armbruster address_item = g_malloc0(sizeof(*address_item)); 105210a2158fSMarkus Armbruster address_item->value = g_malloc0(sizeof(*address_item->value)); 10533424fc9fSMichal Privoznik address_item->value->ip_address = g_strdup(addr4); 10543424fc9fSMichal Privoznik address_item->value->ip_address_type = GUEST_IP_ADDRESS_TYPE_IPV4; 10553424fc9fSMichal Privoznik 10563424fc9fSMichal Privoznik if (ifa->ifa_netmask) { 10573424fc9fSMichal Privoznik /* Count the number of set bits in netmask. 10583424fc9fSMichal Privoznik * This is safe as '1' and '0' cannot be shuffled in netmask. */ 10593424fc9fSMichal Privoznik p = &((struct sockaddr_in *)ifa->ifa_netmask)->sin_addr; 10603424fc9fSMichal Privoznik address_item->value->prefix = ctpop32(((uint32_t *) p)[0]); 10613424fc9fSMichal Privoznik } 10623424fc9fSMichal Privoznik } else if (ifa->ifa_addr && 10633424fc9fSMichal Privoznik ifa->ifa_addr->sa_family == AF_INET6) { 10643424fc9fSMichal Privoznik /* interface with IPv6 address */ 10653424fc9fSMichal Privoznik p = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr; 10663424fc9fSMichal Privoznik if (!inet_ntop(AF_INET6, p, addr6, sizeof(addr6))) { 1067878a0ae0SLuiz Capitulino error_setg_errno(errp, errno, "inet_ntop failed"); 10683424fc9fSMichal Privoznik goto error; 10693424fc9fSMichal Privoznik } 10703424fc9fSMichal Privoznik 107110a2158fSMarkus Armbruster address_item = g_malloc0(sizeof(*address_item)); 107210a2158fSMarkus Armbruster address_item->value = g_malloc0(sizeof(*address_item->value)); 10733424fc9fSMichal Privoznik address_item->value->ip_address = g_strdup(addr6); 10743424fc9fSMichal Privoznik address_item->value->ip_address_type = GUEST_IP_ADDRESS_TYPE_IPV6; 10753424fc9fSMichal Privoznik 10763424fc9fSMichal Privoznik if (ifa->ifa_netmask) { 10773424fc9fSMichal Privoznik /* Count the number of set bits in netmask. 10783424fc9fSMichal Privoznik * This is safe as '1' and '0' cannot be shuffled in netmask. */ 10793424fc9fSMichal Privoznik p = &((struct sockaddr_in6 *)ifa->ifa_netmask)->sin6_addr; 10803424fc9fSMichal Privoznik address_item->value->prefix = 10813424fc9fSMichal Privoznik ctpop32(((uint32_t *) p)[0]) + 10823424fc9fSMichal Privoznik ctpop32(((uint32_t *) p)[1]) + 10833424fc9fSMichal Privoznik ctpop32(((uint32_t *) p)[2]) + 10843424fc9fSMichal Privoznik ctpop32(((uint32_t *) p)[3]); 10853424fc9fSMichal Privoznik } 10863424fc9fSMichal Privoznik } 10873424fc9fSMichal Privoznik 10883424fc9fSMichal Privoznik if (!address_item) { 10893424fc9fSMichal Privoznik continue; 10903424fc9fSMichal Privoznik } 10913424fc9fSMichal Privoznik 10923424fc9fSMichal Privoznik address_list = &info->value->ip_addresses; 10933424fc9fSMichal Privoznik 10943424fc9fSMichal Privoznik while (*address_list && (*address_list)->next) { 10953424fc9fSMichal Privoznik address_list = &(*address_list)->next; 10963424fc9fSMichal Privoznik } 10973424fc9fSMichal Privoznik 10983424fc9fSMichal Privoznik if (!*address_list) { 10993424fc9fSMichal Privoznik *address_list = address_item; 11003424fc9fSMichal Privoznik } else { 11013424fc9fSMichal Privoznik (*address_list)->next = address_item; 11023424fc9fSMichal Privoznik } 11033424fc9fSMichal Privoznik 11043424fc9fSMichal Privoznik info->value->has_ip_addresses = true; 11053424fc9fSMichal Privoznik 11063424fc9fSMichal Privoznik 11073424fc9fSMichal Privoznik } 11083424fc9fSMichal Privoznik 11093424fc9fSMichal Privoznik freeifaddrs(ifap); 11103424fc9fSMichal Privoznik return head; 11113424fc9fSMichal Privoznik 11123424fc9fSMichal Privoznik error: 11133424fc9fSMichal Privoznik freeifaddrs(ifap); 11143424fc9fSMichal Privoznik qapi_free_GuestNetworkInterfaceList(head); 11153424fc9fSMichal Privoznik return NULL; 11163424fc9fSMichal Privoznik } 11173424fc9fSMichal Privoznik 1118d2baff62SLaszlo Ersek #define SYSCONF_EXACT(name, err) sysconf_exact((name), #name, (err)) 1119d2baff62SLaszlo Ersek 1120d2baff62SLaszlo Ersek static long sysconf_exact(int name, const char *name_str, Error **err) 1121d2baff62SLaszlo Ersek { 1122d2baff62SLaszlo Ersek long ret; 1123d2baff62SLaszlo Ersek 1124d2baff62SLaszlo Ersek errno = 0; 1125d2baff62SLaszlo Ersek ret = sysconf(name); 1126d2baff62SLaszlo Ersek if (ret == -1) { 1127d2baff62SLaszlo Ersek if (errno == 0) { 1128d2baff62SLaszlo Ersek error_setg(err, "sysconf(%s): value indefinite", name_str); 1129d2baff62SLaszlo Ersek } else { 1130d2baff62SLaszlo Ersek error_setg_errno(err, errno, "sysconf(%s)", name_str); 1131d2baff62SLaszlo Ersek } 1132d2baff62SLaszlo Ersek } 1133d2baff62SLaszlo Ersek return ret; 1134d2baff62SLaszlo Ersek } 1135d2baff62SLaszlo Ersek 1136d2baff62SLaszlo Ersek /* Transfer online/offline status between @vcpu and the guest system. 1137d2baff62SLaszlo Ersek * 1138d2baff62SLaszlo Ersek * On input either @errp or *@errp must be NULL. 1139d2baff62SLaszlo Ersek * 1140d2baff62SLaszlo Ersek * In system-to-@vcpu direction, the following @vcpu fields are accessed: 1141d2baff62SLaszlo Ersek * - R: vcpu->logical_id 1142d2baff62SLaszlo Ersek * - W: vcpu->online 1143d2baff62SLaszlo Ersek * - W: vcpu->can_offline 1144d2baff62SLaszlo Ersek * 1145d2baff62SLaszlo Ersek * In @vcpu-to-system direction, the following @vcpu fields are accessed: 1146d2baff62SLaszlo Ersek * - R: vcpu->logical_id 1147d2baff62SLaszlo Ersek * - R: vcpu->online 1148d2baff62SLaszlo Ersek * 1149d2baff62SLaszlo Ersek * Written members remain unmodified on error. 1150d2baff62SLaszlo Ersek */ 1151d2baff62SLaszlo Ersek static void transfer_vcpu(GuestLogicalProcessor *vcpu, bool sys2vcpu, 1152d2baff62SLaszlo Ersek Error **errp) 1153d2baff62SLaszlo Ersek { 1154d2baff62SLaszlo Ersek char *dirpath; 1155d2baff62SLaszlo Ersek int dirfd; 1156d2baff62SLaszlo Ersek 1157d2baff62SLaszlo Ersek dirpath = g_strdup_printf("/sys/devices/system/cpu/cpu%" PRId64 "/", 1158d2baff62SLaszlo Ersek vcpu->logical_id); 1159d2baff62SLaszlo Ersek dirfd = open(dirpath, O_RDONLY | O_DIRECTORY); 1160d2baff62SLaszlo Ersek if (dirfd == -1) { 1161d2baff62SLaszlo Ersek error_setg_errno(errp, errno, "open(\"%s\")", dirpath); 1162d2baff62SLaszlo Ersek } else { 1163d2baff62SLaszlo Ersek static const char fn[] = "online"; 1164d2baff62SLaszlo Ersek int fd; 1165d2baff62SLaszlo Ersek int res; 1166d2baff62SLaszlo Ersek 1167d2baff62SLaszlo Ersek fd = openat(dirfd, fn, sys2vcpu ? O_RDONLY : O_RDWR); 1168d2baff62SLaszlo Ersek if (fd == -1) { 1169d2baff62SLaszlo Ersek if (errno != ENOENT) { 1170d2baff62SLaszlo Ersek error_setg_errno(errp, errno, "open(\"%s/%s\")", dirpath, fn); 1171d2baff62SLaszlo Ersek } else if (sys2vcpu) { 1172d2baff62SLaszlo Ersek vcpu->online = true; 1173d2baff62SLaszlo Ersek vcpu->can_offline = false; 1174d2baff62SLaszlo Ersek } else if (!vcpu->online) { 1175d2baff62SLaszlo Ersek error_setg(errp, "logical processor #%" PRId64 " can't be " 1176d2baff62SLaszlo Ersek "offlined", vcpu->logical_id); 1177d2baff62SLaszlo Ersek } /* otherwise pretend successful re-onlining */ 1178d2baff62SLaszlo Ersek } else { 1179d2baff62SLaszlo Ersek unsigned char status; 1180d2baff62SLaszlo Ersek 1181d2baff62SLaszlo Ersek res = pread(fd, &status, 1, 0); 1182d2baff62SLaszlo Ersek if (res == -1) { 1183d2baff62SLaszlo Ersek error_setg_errno(errp, errno, "pread(\"%s/%s\")", dirpath, fn); 1184d2baff62SLaszlo Ersek } else if (res == 0) { 1185d2baff62SLaszlo Ersek error_setg(errp, "pread(\"%s/%s\"): unexpected EOF", dirpath, 1186d2baff62SLaszlo Ersek fn); 1187d2baff62SLaszlo Ersek } else if (sys2vcpu) { 1188d2baff62SLaszlo Ersek vcpu->online = (status != '0'); 1189d2baff62SLaszlo Ersek vcpu->can_offline = true; 1190d2baff62SLaszlo Ersek } else if (vcpu->online != (status != '0')) { 1191d2baff62SLaszlo Ersek status = '0' + vcpu->online; 1192d2baff62SLaszlo Ersek if (pwrite(fd, &status, 1, 0) == -1) { 1193d2baff62SLaszlo Ersek error_setg_errno(errp, errno, "pwrite(\"%s/%s\")", dirpath, 1194d2baff62SLaszlo Ersek fn); 1195d2baff62SLaszlo Ersek } 1196d2baff62SLaszlo Ersek } /* otherwise pretend successful re-(on|off)-lining */ 1197d2baff62SLaszlo Ersek 1198d2baff62SLaszlo Ersek res = close(fd); 1199d2baff62SLaszlo Ersek g_assert(res == 0); 1200d2baff62SLaszlo Ersek } 1201d2baff62SLaszlo Ersek 1202d2baff62SLaszlo Ersek res = close(dirfd); 1203d2baff62SLaszlo Ersek g_assert(res == 0); 1204d2baff62SLaszlo Ersek } 1205d2baff62SLaszlo Ersek 1206d2baff62SLaszlo Ersek g_free(dirpath); 1207d2baff62SLaszlo Ersek } 1208d2baff62SLaszlo Ersek 1209d2baff62SLaszlo Ersek GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp) 1210d2baff62SLaszlo Ersek { 1211d2baff62SLaszlo Ersek int64_t current; 1212d2baff62SLaszlo Ersek GuestLogicalProcessorList *head, **link; 1213d2baff62SLaszlo Ersek long sc_max; 1214d2baff62SLaszlo Ersek Error *local_err = NULL; 1215d2baff62SLaszlo Ersek 1216d2baff62SLaszlo Ersek current = 0; 1217d2baff62SLaszlo Ersek head = NULL; 1218d2baff62SLaszlo Ersek link = &head; 1219d2baff62SLaszlo Ersek sc_max = SYSCONF_EXACT(_SC_NPROCESSORS_CONF, &local_err); 1220d2baff62SLaszlo Ersek 1221d2baff62SLaszlo Ersek while (local_err == NULL && current < sc_max) { 1222d2baff62SLaszlo Ersek GuestLogicalProcessor *vcpu; 1223d2baff62SLaszlo Ersek GuestLogicalProcessorList *entry; 1224d2baff62SLaszlo Ersek 1225d2baff62SLaszlo Ersek vcpu = g_malloc0(sizeof *vcpu); 1226d2baff62SLaszlo Ersek vcpu->logical_id = current++; 1227d2baff62SLaszlo Ersek vcpu->has_can_offline = true; /* lolspeak ftw */ 1228d2baff62SLaszlo Ersek transfer_vcpu(vcpu, true, &local_err); 1229d2baff62SLaszlo Ersek 1230d2baff62SLaszlo Ersek entry = g_malloc0(sizeof *entry); 1231d2baff62SLaszlo Ersek entry->value = vcpu; 1232d2baff62SLaszlo Ersek 1233d2baff62SLaszlo Ersek *link = entry; 1234d2baff62SLaszlo Ersek link = &entry->next; 1235d2baff62SLaszlo Ersek } 1236d2baff62SLaszlo Ersek 1237d2baff62SLaszlo Ersek if (local_err == NULL) { 1238d2baff62SLaszlo Ersek /* there's no guest with zero VCPUs */ 1239d2baff62SLaszlo Ersek g_assert(head != NULL); 1240d2baff62SLaszlo Ersek return head; 1241d2baff62SLaszlo Ersek } 1242d2baff62SLaszlo Ersek 1243d2baff62SLaszlo Ersek qapi_free_GuestLogicalProcessorList(head); 1244d2baff62SLaszlo Ersek error_propagate(errp, local_err); 1245d2baff62SLaszlo Ersek return NULL; 1246d2baff62SLaszlo Ersek } 1247d2baff62SLaszlo Ersek 1248*cbb65fc2SLaszlo Ersek int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp) 1249*cbb65fc2SLaszlo Ersek { 1250*cbb65fc2SLaszlo Ersek int64_t processed; 1251*cbb65fc2SLaszlo Ersek Error *local_err = NULL; 1252*cbb65fc2SLaszlo Ersek 1253*cbb65fc2SLaszlo Ersek processed = 0; 1254*cbb65fc2SLaszlo Ersek while (vcpus != NULL) { 1255*cbb65fc2SLaszlo Ersek transfer_vcpu(vcpus->value, false, &local_err); 1256*cbb65fc2SLaszlo Ersek if (local_err != NULL) { 1257*cbb65fc2SLaszlo Ersek break; 1258*cbb65fc2SLaszlo Ersek } 1259*cbb65fc2SLaszlo Ersek ++processed; 1260*cbb65fc2SLaszlo Ersek vcpus = vcpus->next; 1261*cbb65fc2SLaszlo Ersek } 1262*cbb65fc2SLaszlo Ersek 1263*cbb65fc2SLaszlo Ersek if (local_err != NULL) { 1264*cbb65fc2SLaszlo Ersek if (processed == 0) { 1265*cbb65fc2SLaszlo Ersek error_propagate(errp, local_err); 1266*cbb65fc2SLaszlo Ersek } else { 1267*cbb65fc2SLaszlo Ersek error_free(local_err); 1268*cbb65fc2SLaszlo Ersek } 1269*cbb65fc2SLaszlo Ersek } 1270*cbb65fc2SLaszlo Ersek 1271*cbb65fc2SLaszlo Ersek return processed; 1272*cbb65fc2SLaszlo Ersek } 1273*cbb65fc2SLaszlo Ersek 1274e72c3f2eSMichael Roth #else /* defined(__linux__) */ 1275e72c3f2eSMichael Roth 1276e72c3f2eSMichael Roth void qmp_guest_suspend_disk(Error **err) 1277e72c3f2eSMichael Roth { 1278e72c3f2eSMichael Roth error_set(err, QERR_UNSUPPORTED); 1279e72c3f2eSMichael Roth } 1280e72c3f2eSMichael Roth 1281e72c3f2eSMichael Roth void qmp_guest_suspend_ram(Error **err) 1282e72c3f2eSMichael Roth { 1283e72c3f2eSMichael Roth error_set(err, QERR_UNSUPPORTED); 1284e72c3f2eSMichael Roth } 1285e72c3f2eSMichael Roth 1286e72c3f2eSMichael Roth void qmp_guest_suspend_hybrid(Error **err) 1287e72c3f2eSMichael Roth { 1288e72c3f2eSMichael Roth error_set(err, QERR_UNSUPPORTED); 1289e72c3f2eSMichael Roth } 1290e72c3f2eSMichael Roth 1291e72c3f2eSMichael Roth GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp) 1292e72c3f2eSMichael Roth { 1293e72c3f2eSMichael Roth error_set(errp, QERR_UNSUPPORTED); 1294e72c3f2eSMichael Roth return NULL; 1295e72c3f2eSMichael Roth } 1296e72c3f2eSMichael Roth 1297d2baff62SLaszlo Ersek GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp) 1298d2baff62SLaszlo Ersek { 1299d2baff62SLaszlo Ersek error_set(errp, QERR_UNSUPPORTED); 1300d2baff62SLaszlo Ersek return NULL; 1301d2baff62SLaszlo Ersek } 1302d2baff62SLaszlo Ersek 1303*cbb65fc2SLaszlo Ersek int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp) 1304*cbb65fc2SLaszlo Ersek { 1305*cbb65fc2SLaszlo Ersek error_set(errp, QERR_UNSUPPORTED); 1306*cbb65fc2SLaszlo Ersek return -1; 1307*cbb65fc2SLaszlo Ersek } 1308*cbb65fc2SLaszlo Ersek 1309e72c3f2eSMichael Roth #endif 1310e72c3f2eSMichael Roth 1311d35d4cb5SMichael Roth #if !defined(CONFIG_FSFREEZE) 1312d35d4cb5SMichael Roth 1313d35d4cb5SMichael Roth GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **err) 1314d35d4cb5SMichael Roth { 1315d35d4cb5SMichael Roth error_set(err, QERR_UNSUPPORTED); 1316d35d4cb5SMichael Roth 1317d35d4cb5SMichael Roth return 0; 1318d35d4cb5SMichael Roth } 1319d35d4cb5SMichael Roth 1320d35d4cb5SMichael Roth int64_t qmp_guest_fsfreeze_freeze(Error **err) 1321d35d4cb5SMichael Roth { 1322d35d4cb5SMichael Roth error_set(err, QERR_UNSUPPORTED); 1323d35d4cb5SMichael Roth 1324d35d4cb5SMichael Roth return 0; 1325d35d4cb5SMichael Roth } 1326d35d4cb5SMichael Roth 1327d35d4cb5SMichael Roth int64_t qmp_guest_fsfreeze_thaw(Error **err) 1328d35d4cb5SMichael Roth { 1329d35d4cb5SMichael Roth error_set(err, QERR_UNSUPPORTED); 1330d35d4cb5SMichael Roth 1331d35d4cb5SMichael Roth return 0; 1332d35d4cb5SMichael Roth } 1333eab5fd59SPaolo Bonzini #endif /* CONFIG_FSFREEZE */ 1334d35d4cb5SMichael Roth 1335eab5fd59SPaolo Bonzini #if !defined(CONFIG_FSTRIM) 1336eab5fd59SPaolo Bonzini void qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **err) 1337eab5fd59SPaolo Bonzini { 1338eab5fd59SPaolo Bonzini error_set(err, QERR_UNSUPPORTED); 1339eab5fd59SPaolo Bonzini } 1340d35d4cb5SMichael Roth #endif 1341d35d4cb5SMichael Roth 1342c216e5adSMichael Roth /* register init/cleanup routines for stateful command groups */ 1343c216e5adSMichael Roth void ga_command_state_init(GAState *s, GACommandState *cs) 1344c216e5adSMichael Roth { 1345c216e5adSMichael Roth #if defined(CONFIG_FSFREEZE) 1346f22d85e9SMichael Roth ga_command_state_add(cs, NULL, guest_fsfreeze_cleanup); 1347c216e5adSMichael Roth #endif 1348c216e5adSMichael Roth ga_command_state_add(cs, guest_file_init, NULL); 1349c216e5adSMichael Roth } 1350