commands-posix.c 40.2 KB
Newer Older
1
/*
2
 * QEMU Guest Agent POSIX-specific command implementations
3 4 5 6 7
 *
 * Copyright IBM Corp. 2011
 *
 * Authors:
 *  Michael Roth      <mdroth@linux.vnet.ibm.com>
8
 *  Michal Privoznik  <mprivozn@redhat.com>
9 10 11 12 13 14
 *
 * This work is licensed under the terms of the GNU GPL, version 2 or later.
 * See the COPYING file in the top-level directory.
 */

#include <glib.h>
15 16
#include <sys/types.h>
#include <sys/ioctl.h>
17
#include <sys/wait.h>
18 19 20
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
21 22 23
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
24
#include <inttypes.h>
25 26
#include "qga/guest-agent-core.h"
#include "qga-qmp-commands.h"
27
#include "qapi/qmp/qerror.h"
28 29
#include "qemu/queue.h"
#include "qemu/host-utils.h"
30

31
#ifndef CONFIG_HAS_ENVIRON
32 33 34 35
#ifdef __APPLE__
#include <crt_externs.h>
#define environ (*_NSGetEnviron())
#else
36 37
extern char **environ;
#endif
38
#endif
39

40
#if defined(__linux__)
41
#include <mntent.h>
42
#include <linux/fs.h>
43 44 45 46
#include <ifaddrs.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <net/if.h>
47

48
#ifdef FIFREEZE
49 50
#define CONFIG_FSFREEZE
#endif
51 52 53
#ifdef FITRIM
#define CONFIG_FSTRIM
#endif
54 55
#endif

56
static void ga_wait_child(pid_t pid, int *status, Error **errp)
57 58 59 60 61 62 63 64 65 66
{
    pid_t rpid;

    *status = 0;

    do {
        rpid = waitpid(pid, status, 0);
    } while (rpid == -1 && errno == EINTR);

    if (rpid == -1) {
67 68
        error_setg_errno(errp, errno, "failed to wait for child (pid: %d)",
                         pid);
69 70 71 72 73 74
        return;
    }

    g_assert(rpid == pid);
}

75
void qmp_guest_shutdown(bool has_mode, const char *mode, Error **errp)
76 77
{
    const char *shutdown_flag;
78 79
    Error *local_err = NULL;
    pid_t pid;
80
    int status;
81 82 83 84 85 86 87 88 89

    slog("guest-shutdown called, mode: %s", mode);
    if (!has_mode || strcmp(mode, "powerdown") == 0) {
        shutdown_flag = "-P";
    } else if (strcmp(mode, "halt") == 0) {
        shutdown_flag = "-H";
    } else if (strcmp(mode, "reboot") == 0) {
        shutdown_flag = "-r";
    } else {
90
        error_setg(errp,
91
                   "mode is invalid (valid values are: halt|powerdown|reboot");
92 93 94
        return;
    }

95 96
    pid = fork();
    if (pid == 0) {
97 98
        /* child, start the shutdown */
        setsid();
99 100 101
        reopen_fd_to_null(0);
        reopen_fd_to_null(1);
        reopen_fd_to_null(2);
102

103
        execle("/sbin/shutdown", "shutdown", "-h", shutdown_flag, "+0",
104 105
               "hypervisor initiated shutdown", (char*)NULL, environ);
        _exit(EXIT_FAILURE);
106
    } else if (pid < 0) {
107
        error_setg_errno(errp, errno, "failed to create child process");
108
        return;
109
    }
110

111
    ga_wait_child(pid, &status, &local_err);
112
    if (local_err) {
113
        error_propagate(errp, local_err);
114 115 116 117
        return;
    }

    if (!WIFEXITED(status)) {
118
        error_setg(errp, "child process has terminated abnormally");
119 120 121 122
        return;
    }

    if (WEXITSTATUS(status)) {
123
        error_setg(errp, "child process has failed to shutdown");
124 125 126
        return;
    }

P
Peter Maydell 已提交
127
    /* succeeded */
128 129
}

L
Lei Li 已提交
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
int64_t qmp_guest_get_time(Error **errp)
{
   int ret;
   qemu_timeval tq;
   int64_t time_ns;

   ret = qemu_gettimeofday(&tq);
   if (ret < 0) {
       error_setg_errno(errp, errno, "Failed to get time");
       return -1;
   }

   time_ns = tq.tv_sec * 1000000000LL + tq.tv_usec * 1000;
   return time_ns;
}

146
void qmp_guest_set_time(bool has_time, int64_t time_ns, Error **errp)
L
Lei Li 已提交
147 148 149 150 151 152 153
{
    int ret;
    int status;
    pid_t pid;
    Error *local_err = NULL;
    struct timeval tv;

154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
    /* If user has passed a time, validate and set it. */
    if (has_time) {
        /* year-2038 will overflow in case time_t is 32bit */
        if (time_ns / 1000000000 != (time_t)(time_ns / 1000000000)) {
            error_setg(errp, "Time %" PRId64 " is too large", time_ns);
            return;
        }

        tv.tv_sec = time_ns / 1000000000;
        tv.tv_usec = (time_ns % 1000000000) / 1000;

        ret = settimeofday(&tv, NULL);
        if (ret < 0) {
            error_setg_errno(errp, errno, "Failed to set time to guest");
            return;
        }
L
Lei Li 已提交
170 171
    }

172 173 174
    /* Now, if user has passed a time to set and the system time is set, we
     * just need to synchronize the hardware clock. However, if no time was
     * passed, user is requesting the opposite: set the system time from the
175
     * hardware clock (RTC). */
L
Lei Li 已提交
176 177 178 179 180 181 182
    pid = fork();
    if (pid == 0) {
        setsid();
        reopen_fd_to_null(0);
        reopen_fd_to_null(1);
        reopen_fd_to_null(2);

183 184 185 186
        /* Use '/sbin/hwclock -w' to set RTC from the system time,
         * or '/sbin/hwclock -s' to set the system time from RTC. */
        execle("/sbin/hwclock", "hwclock", has_time ? "-w" : "-s",
               NULL, environ);
L
Lei Li 已提交
187 188 189 190 191 192 193
        _exit(EXIT_FAILURE);
    } else if (pid < 0) {
        error_setg_errno(errp, errno, "failed to create child process");
        return;
    }

    ga_wait_child(pid, &status, &local_err);
194
    if (local_err) {
L
Lei Li 已提交
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209
        error_propagate(errp, local_err);
        return;
    }

    if (!WIFEXITED(status)) {
        error_setg(errp, "child process has terminated abnormally");
        return;
    }

    if (WEXITSTATUS(status)) {
        error_setg(errp, "hwclock failed to set hardware clock to system time");
        return;
    }
}

210 211 212 213 214 215 216 217 218 219
typedef struct GuestFileHandle {
    uint64_t id;
    FILE *fh;
    QTAILQ_ENTRY(GuestFileHandle) next;
} GuestFileHandle;

static struct {
    QTAILQ_HEAD(, GuestFileHandle) filehandles;
} guest_file_state;

220
static int64_t guest_file_handle_add(FILE *fh, Error **errp)
221 222
{
    GuestFileHandle *gfh;
223 224 225
    int64_t handle;

    handle = ga_get_fd_handle(ga_state, errp);
226 227
    if (handle < 0) {
        return -1;
228
    }
229

230
    gfh = g_malloc0(sizeof(GuestFileHandle));
231
    gfh->id = handle;
232 233
    gfh->fh = fh;
    QTAILQ_INSERT_TAIL(&guest_file_state.filehandles, gfh, next);
234 235

    return handle;
236 237
}

238
static GuestFileHandle *guest_file_handle_find(int64_t id, Error **errp)
239 240 241 242 243 244 245 246 247 248
{
    GuestFileHandle *gfh;

    QTAILQ_FOREACH(gfh, &guest_file_state.filehandles, next)
    {
        if (gfh->id == id) {
            return gfh;
        }
    }

249
    error_setg(errp, "handle '%" PRId64 "' has not been found", id);
250 251 252
    return NULL;
}

253 254
typedef const char * const ccpc;

255 256 257 258
#ifndef O_BINARY
#define O_BINARY 0
#endif

259 260 261 262 263
/* http://pubs.opengroup.org/onlinepubs/9699919799/functions/fopen.html */
static const struct {
    ccpc *forms;
    int oflag_base;
} guest_file_open_modes[] = {
264 265 266 267 268 269 270 271 272 273 274 275
    { (ccpc[]){ "r",          NULL }, O_RDONLY                                 },
    { (ccpc[]){ "rb",         NULL }, O_RDONLY                      | O_BINARY },
    { (ccpc[]){ "w",          NULL }, O_WRONLY | O_CREAT | O_TRUNC             },
    { (ccpc[]){ "wb",         NULL }, O_WRONLY | O_CREAT | O_TRUNC  | O_BINARY },
    { (ccpc[]){ "a",          NULL }, O_WRONLY | O_CREAT | O_APPEND            },
    { (ccpc[]){ "ab",         NULL }, O_WRONLY | O_CREAT | O_APPEND | O_BINARY },
    { (ccpc[]){ "r+",         NULL }, O_RDWR                                   },
    { (ccpc[]){ "rb+", "r+b", NULL }, O_RDWR                        | O_BINARY },
    { (ccpc[]){ "w+",         NULL }, O_RDWR   | O_CREAT | O_TRUNC             },
    { (ccpc[]){ "wb+", "w+b", NULL }, O_RDWR   | O_CREAT | O_TRUNC  | O_BINARY },
    { (ccpc[]){ "a+",         NULL }, O_RDWR   | O_CREAT | O_APPEND            },
    { (ccpc[]){ "ab+", "a+b", NULL }, O_RDWR   | O_CREAT | O_APPEND | O_BINARY }
276 277 278
};

static int
279
find_open_flag(const char *mode_str, Error **errp)
280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295
{
    unsigned mode;

    for (mode = 0; mode < ARRAY_SIZE(guest_file_open_modes); ++mode) {
        ccpc *form;

        form = guest_file_open_modes[mode].forms;
        while (*form != NULL && strcmp(*form, mode_str) != 0) {
            ++form;
        }
        if (*form != NULL) {
            break;
        }
    }

    if (mode == ARRAY_SIZE(guest_file_open_modes)) {
296
        error_setg(errp, "invalid file open mode '%s'", mode_str);
297 298 299 300 301 302 303 304 305 306
        return -1;
    }
    return guest_file_open_modes[mode].oflag_base | O_NOCTTY | O_NONBLOCK;
}

#define DEFAULT_NEW_FILE_MODE (S_IRUSR | S_IWUSR | \
                               S_IRGRP | S_IWGRP | \
                               S_IROTH | S_IWOTH)

static FILE *
307
safe_open_or_create(const char *path, const char *mode, Error **errp)
308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367
{
    Error *local_err = NULL;
    int oflag;

    oflag = find_open_flag(mode, &local_err);
    if (local_err == NULL) {
        int fd;

        /* If the caller wants / allows creation of a new file, we implement it
         * with a two step process: open() + (open() / fchmod()).
         *
         * First we insist on creating the file exclusively as a new file. If
         * that succeeds, we're free to set any file-mode bits on it. (The
         * motivation is that we want to set those file-mode bits independently
         * of the current umask.)
         *
         * If the exclusive creation fails because the file already exists
         * (EEXIST is not possible for any other reason), we just attempt to
         * open the file, but in this case we won't be allowed to change the
         * file-mode bits on the preexistent file.
         *
         * The pathname should never disappear between the two open()s in
         * practice. If it happens, then someone very likely tried to race us.
         * In this case just go ahead and report the ENOENT from the second
         * open() to the caller.
         *
         * If the caller wants to open a preexistent file, then the first
         * open() is decisive and its third argument is ignored, and the second
         * open() and the fchmod() are never called.
         */
        fd = open(path, oflag | ((oflag & O_CREAT) ? O_EXCL : 0), 0);
        if (fd == -1 && errno == EEXIST) {
            oflag &= ~(unsigned)O_CREAT;
            fd = open(path, oflag);
        }

        if (fd == -1) {
            error_setg_errno(&local_err, errno, "failed to open file '%s' "
                             "(mode: '%s')", path, mode);
        } else {
            qemu_set_cloexec(fd);

            if ((oflag & O_CREAT) && fchmod(fd, DEFAULT_NEW_FILE_MODE) == -1) {
                error_setg_errno(&local_err, errno, "failed to set permission "
                                 "0%03o on new file '%s' (mode: '%s')",
                                 (unsigned)DEFAULT_NEW_FILE_MODE, path, mode);
            } else {
                FILE *f;

                f = fdopen(fd, mode);
                if (f == NULL) {
                    error_setg_errno(&local_err, errno, "failed to associate "
                                     "stdio stream with file descriptor %d, "
                                     "file '%s' (mode: '%s')", fd, path, mode);
                } else {
                    return f;
                }
            }

            close(fd);
368 369 370
            if (oflag & O_CREAT) {
                unlink(path);
            }
371 372 373
        }
    }

374
    error_propagate(errp, local_err);
375 376 377
    return NULL;
}

378 379
int64_t qmp_guest_file_open(const char *path, bool has_mode, const char *mode,
                            Error **errp)
380 381
{
    FILE *fh;
382
    Error *local_err = NULL;
383
    int fd;
384
    int64_t ret = -1, handle;
385 386 387 388 389

    if (!has_mode) {
        mode = "r";
    }
    slog("guest-file-open called, filepath: %s, mode: %s", path, mode);
390 391
    fh = safe_open_or_create(path, mode, &local_err);
    if (local_err != NULL) {
392
        error_propagate(errp, local_err);
393 394 395 396 397 398 399 400 401 402
        return -1;
    }

    /* set fd non-blocking to avoid common use cases (like reading from a
     * named pipe) from hanging the agent
     */
    fd = fileno(fh);
    ret = fcntl(fd, F_GETFL);
    ret = fcntl(fd, F_SETFL, ret | O_NONBLOCK);
    if (ret == -1) {
403
        error_setg_errno(errp, errno, "failed to make file '%s' non-blocking",
404
                         path);
405 406 407 408
        fclose(fh);
        return -1;
    }

409
    handle = guest_file_handle_add(fh, errp);
410
    if (handle < 0) {
411 412 413 414
        fclose(fh);
        return -1;
    }

415
    slog("guest-file-open, handle: %" PRId64, handle);
416
    return handle;
417 418
}

419
void qmp_guest_file_close(int64_t handle, Error **errp)
420
{
421
    GuestFileHandle *gfh = guest_file_handle_find(handle, errp);
422 423
    int ret;

424
    slog("guest-file-close called, handle: %" PRId64, handle);
425 426 427 428 429
    if (!gfh) {
        return;
    }

    ret = fclose(gfh->fh);
430
    if (ret == EOF) {
431
        error_setg_errno(errp, errno, "failed to close handle");
432 433 434 435
        return;
    }

    QTAILQ_REMOVE(&guest_file_state.filehandles, gfh, next);
436
    g_free(gfh);
437 438 439
}

struct GuestFileRead *qmp_guest_file_read(int64_t handle, bool has_count,
440
                                          int64_t count, Error **errp)
441
{
442
    GuestFileHandle *gfh = guest_file_handle_find(handle, errp);
443 444 445 446 447 448 449 450 451 452 453 454
    GuestFileRead *read_data = NULL;
    guchar *buf;
    FILE *fh;
    size_t read_count;

    if (!gfh) {
        return NULL;
    }

    if (!has_count) {
        count = QGA_READ_COUNT_DEFAULT;
    } else if (count < 0) {
455
        error_setg(errp, "value '%" PRId64 "' is invalid for argument count",
456
                   count);
457 458 459 460
        return NULL;
    }

    fh = gfh->fh;
461
    buf = g_malloc0(count+1);
462 463
    read_count = fread(buf, 1, count, fh);
    if (ferror(fh)) {
464
        error_setg_errno(errp, errno, "failed to read file");
465
        slog("guest-file-read failed, handle: %" PRId64, handle);
466 467
    } else {
        buf[read_count] = 0;
468
        read_data = g_malloc0(sizeof(GuestFileRead));
469 470 471 472 473 474
        read_data->count = read_count;
        read_data->eof = feof(fh);
        if (read_count) {
            read_data->buf_b64 = g_base64_encode(buf, read_count);
        }
    }
475
    g_free(buf);
476 477 478 479 480 481
    clearerr(fh);

    return read_data;
}

GuestFileWrite *qmp_guest_file_write(int64_t handle, const char *buf_b64,
482 483
                                     bool has_count, int64_t count,
                                     Error **errp)
484 485 486 487 488
{
    GuestFileWrite *write_data = NULL;
    guchar *buf;
    gsize buf_len;
    int write_count;
489
    GuestFileHandle *gfh = guest_file_handle_find(handle, errp);
490 491 492 493 494 495 496 497 498 499 500 501
    FILE *fh;

    if (!gfh) {
        return NULL;
    }

    fh = gfh->fh;
    buf = g_base64_decode(buf_b64, &buf_len);

    if (!has_count) {
        count = buf_len;
    } else if (count < 0 || count > buf_len) {
502
        error_setg(errp, "value '%" PRId64 "' is invalid for argument count",
503
                   count);
504
        g_free(buf);
505 506 507 508 509
        return NULL;
    }

    write_count = fwrite(buf, 1, count, fh);
    if (ferror(fh)) {
510
        error_setg_errno(errp, errno, "failed to write to file");
511
        slog("guest-file-write failed, handle: %" PRId64, handle);
512
    } else {
513
        write_data = g_malloc0(sizeof(GuestFileWrite));
514 515 516
        write_data->count = write_count;
        write_data->eof = feof(fh);
    }
517
    g_free(buf);
518 519 520 521 522 523
    clearerr(fh);

    return write_data;
}

struct GuestFileSeek *qmp_guest_file_seek(int64_t handle, int64_t offset,
524
                                          int64_t whence, Error **errp)
525
{
526
    GuestFileHandle *gfh = guest_file_handle_find(handle, errp);
527 528 529 530 531 532 533 534 535 536 537
    GuestFileSeek *seek_data = NULL;
    FILE *fh;
    int ret;

    if (!gfh) {
        return NULL;
    }

    fh = gfh->fh;
    ret = fseek(fh, offset, whence);
    if (ret == -1) {
538
        error_setg_errno(errp, errno, "failed to seek file");
539
    } else {
540
        seek_data = g_new0(GuestFileSeek, 1);
541 542 543 544 545 546 547 548
        seek_data->position = ftell(fh);
        seek_data->eof = feof(fh);
    }
    clearerr(fh);

    return seek_data;
}

549
void qmp_guest_file_flush(int64_t handle, Error **errp)
550
{
551
    GuestFileHandle *gfh = guest_file_handle_find(handle, errp);
552 553 554 555 556 557 558 559 560 561
    FILE *fh;
    int ret;

    if (!gfh) {
        return;
    }

    fh = gfh->fh;
    ret = fflush(fh);
    if (ret == EOF) {
562
        error_setg_errno(errp, errno, "failed to flush file");
563 564 565 566 567 568 569 570
    }
}

static void guest_file_init(void)
{
    QTAILQ_INIT(&guest_file_state.filehandles);
}

571 572 573
/* linux-specific implementations. avoid this if at all possible. */
#if defined(__linux__)

574
#if defined(CONFIG_FSFREEZE) || defined(CONFIG_FSTRIM)
575
typedef struct FsMount {
576 577
    char *dirname;
    char *devtype;
578 579
    QTAILQ_ENTRY(FsMount) next;
} FsMount;
580

581
typedef QTAILQ_HEAD(FsMountList, FsMount) FsMountList;
582

583
static void free_fs_mount_list(FsMountList *mounts)
584
{
585
     FsMount *mount, *temp;
586 587 588 589 590 591 592 593 594 595 596 597 598

     if (!mounts) {
         return;
     }

     QTAILQ_FOREACH_SAFE(mount, mounts, next, temp) {
         QTAILQ_REMOVE(mounts, mount, next);
         g_free(mount->dirname);
         g_free(mount->devtype);
         g_free(mount);
     }
}

599 600 601
/*
 * Walk the mount table and build a list of local file systems
 */
602
static void build_fs_mount_list(FsMountList *mounts, Error **errp)
603 604
{
    struct mntent *ment;
605
    FsMount *mount;
606
    char const *mtab = "/proc/self/mounts";
607 608 609 610
    FILE *fp;

    fp = setmntent(mtab, "r");
    if (!fp) {
611
        error_setg(errp, "failed to open mtab file: '%s'", mtab);
612
        return;
613 614 615 616 617 618 619 620 621 622 623 624 625 626 627
    }

    while ((ment = getmntent(fp))) {
        /*
         * An entry which device name doesn't start with a '/' is
         * either a dummy file system or a network file system.
         * Add special handling for smbfs and cifs as is done by
         * coreutils as well.
         */
        if ((ment->mnt_fsname[0] != '/') ||
            (strcmp(ment->mnt_type, "smbfs") == 0) ||
            (strcmp(ment->mnt_type, "cifs") == 0)) {
            continue;
        }

628
        mount = g_malloc0(sizeof(FsMount));
629 630
        mount->dirname = g_strdup(ment->mnt_dir);
        mount->devtype = g_strdup(ment->mnt_type);
631

632
        QTAILQ_INSERT_TAIL(mounts, mount, next);
633 634 635 636
    }

    endmntent(fp);
}
637 638 639
#endif

#if defined(CONFIG_FSFREEZE)
640

641 642 643 644 645 646 647 648 649 650
typedef enum {
    FSFREEZE_HOOK_THAW = 0,
    FSFREEZE_HOOK_FREEZE,
} FsfreezeHookArg;

const char *fsfreeze_hook_arg_string[] = {
    "thaw",
    "freeze",
};

651
static void execute_fsfreeze_hook(FsfreezeHookArg arg, Error **errp)
652 653 654 655 656 657 658 659 660 661 662 663
{
    int status;
    pid_t pid;
    const char *hook;
    const char *arg_str = fsfreeze_hook_arg_string[arg];
    Error *local_err = NULL;

    hook = ga_fsfreeze_hook(ga_state);
    if (!hook) {
        return;
    }
    if (access(hook, X_OK) != 0) {
664
        error_setg_errno(errp, errno, "can't access fsfreeze hook '%s'", hook);
665 666 667 668 669 670 671 672 673 674 675 676 677 678
        return;
    }

    slog("executing fsfreeze hook with arg '%s'", arg_str);
    pid = fork();
    if (pid == 0) {
        setsid();
        reopen_fd_to_null(0);
        reopen_fd_to_null(1);
        reopen_fd_to_null(2);

        execle(hook, hook, arg_str, NULL, environ);
        _exit(EXIT_FAILURE);
    } else if (pid < 0) {
679
        error_setg_errno(errp, errno, "failed to create child process");
680 681 682 683
        return;
    }

    ga_wait_child(pid, &status, &local_err);
684
    if (local_err) {
685
        error_propagate(errp, local_err);
686 687 688 689
        return;
    }

    if (!WIFEXITED(status)) {
690
        error_setg(errp, "fsfreeze hook has terminated abnormally");
691 692 693 694 695
        return;
    }

    status = WEXITSTATUS(status);
    if (status) {
696
        error_setg(errp, "fsfreeze hook has failed with status %d", status);
697 698 699 700
        return;
    }
}

701 702 703
/*
 * Return status of freeze/thaw
 */
704
GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **errp)
705
{
706 707 708 709 710
    if (ga_is_frozen(ga_state)) {
        return GUEST_FSFREEZE_STATUS_FROZEN;
    }

    return GUEST_FSFREEZE_STATUS_THAWED;
711 712 713 714 715 716
}

/*
 * Walk list of mounted file systems in the guest, and freeze the ones which
 * are real local file systems.
 */
717
int64_t qmp_guest_fsfreeze_freeze(Error **errp)
718 719
{
    int ret = 0, i = 0;
720 721
    FsMountList mounts;
    struct FsMount *mount;
722
    Error *local_err = NULL;
723 724 725 726
    int fd;

    slog("guest-fsfreeze called");

727
    execute_fsfreeze_hook(FSFREEZE_HOOK_FREEZE, &local_err);
728
    if (local_err) {
729
        error_propagate(errp, local_err);
730 731 732
        return -1;
    }

733
    QTAILQ_INIT(&mounts);
734
    build_fs_mount_list(&mounts, &local_err);
735
    if (local_err) {
736
        error_propagate(errp, local_err);
737
        return -1;
738 739 740
    }

    /* cannot risk guest agent blocking itself on a write in this state */
741
    ga_set_frozen(ga_state);
742

743
    QTAILQ_FOREACH_REVERSE(mount, &mounts, FsMountList, next) {
744 745
        fd = qemu_open(mount->dirname, O_RDONLY);
        if (fd == -1) {
746
            error_setg_errno(errp, errno, "failed to open %s", mount->dirname);
747 748 749 750 751
            goto error;
        }

        /* we try to cull filesytems we know won't work in advance, but other
         * filesytems may not implement fsfreeze for less obvious reasons.
752 753 754 755 756 757
         * these will report EOPNOTSUPP. we simply ignore these when tallying
         * the number of frozen filesystems.
         *
         * any other error means a failure to freeze a filesystem we
         * expect to be freezable, so return an error in those cases
         * and return system to thawed state.
758 759
         */
        ret = ioctl(fd, FIFREEZE);
760 761
        if (ret == -1) {
            if (errno != EOPNOTSUPP) {
762
                error_setg_errno(errp, errno, "failed to freeze %s",
763
                                 mount->dirname);
764 765 766 767 768
                close(fd);
                goto error;
            }
        } else {
            i++;
769 770 771 772
        }
        close(fd);
    }

773
    free_fs_mount_list(&mounts);
774 775 776
    return i;

error:
777
    free_fs_mount_list(&mounts);
778
    qmp_guest_fsfreeze_thaw(NULL);
779 780 781 782 783 784
    return 0;
}

/*
 * Walk list of frozen file systems in the guest, and thaw them.
 */
785
int64_t qmp_guest_fsfreeze_thaw(Error **errp)
786 787
{
    int ret;
788 789
    FsMountList mounts;
    FsMount *mount;
790
    int fd, i = 0, logged;
791
    Error *local_err = NULL;
792 793

    QTAILQ_INIT(&mounts);
794
    build_fs_mount_list(&mounts, &local_err);
795
    if (local_err) {
796
        error_propagate(errp, local_err);
797 798
        return 0;
    }
799

800 801
    QTAILQ_FOREACH(mount, &mounts, next) {
        logged = false;
802 803 804 805
        fd = qemu_open(mount->dirname, O_RDONLY);
        if (fd == -1) {
            continue;
        }
806 807 808 809 810
        /* we have no way of knowing whether a filesystem was actually unfrozen
         * as a result of a successful call to FITHAW, only that if an error
         * was returned the filesystem was *not* unfrozen by that particular
         * call.
         *
J
Jim Meyering 已提交
811
         * since multiple preceding FIFREEZEs require multiple calls to FITHAW
812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828
         * to unfreeze, continuing issuing FITHAW until an error is returned,
         * in which case either the filesystem is in an unfreezable state, or,
         * more likely, it was thawed previously (and remains so afterward).
         *
         * also, since the most recent successful call is the one that did
         * the actual unfreeze, we can use this to provide an accurate count
         * of the number of filesystems unfrozen by guest-fsfreeze-thaw, which
         * may * be useful for determining whether a filesystem was unfrozen
         * during the freeze/thaw phase by a process other than qemu-ga.
         */
        do {
            ret = ioctl(fd, FITHAW);
            if (ret == 0 && !logged) {
                i++;
                logged = true;
            }
        } while (ret == 0);
829 830 831
        close(fd);
    }

832
    ga_unset_frozen(ga_state);
833
    free_fs_mount_list(&mounts);
834

835
    execute_fsfreeze_hook(FSFREEZE_HOOK_THAW, errp);
836

837 838 839 840 841 842 843
    return i;
}

static void guest_fsfreeze_cleanup(void)
{
    Error *err = NULL;

844
    if (ga_is_frozen(ga_state) == GUEST_FSFREEZE_STATUS_FROZEN) {
845 846 847 848 849
        qmp_guest_fsfreeze_thaw(&err);
        if (err) {
            slog("failed to clean up frozen filesystems: %s",
                 error_get_pretty(err));
            error_free(err);
850 851 852
        }
    }
}
853
#endif /* CONFIG_FSFREEZE */
854

855 856 857 858
#if defined(CONFIG_FSTRIM)
/*
 * Walk list of mounted file systems in the guest, and trim them.
 */
859
void qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp)
860 861 862 863 864
{
    int ret = 0;
    FsMountList mounts;
    struct FsMount *mount;
    int fd;
865
    Error *local_err = NULL;
866 867 868 869 870 871 872 873 874
    struct fstrim_range r = {
        .start = 0,
        .len = -1,
        .minlen = has_minimum ? minimum : 0,
    };

    slog("guest-fstrim called");

    QTAILQ_INIT(&mounts);
875
    build_fs_mount_list(&mounts, &local_err);
876
    if (local_err) {
877
        error_propagate(errp, local_err);
878 879 880 881 882 883
        return;
    }

    QTAILQ_FOREACH(mount, &mounts, next) {
        fd = qemu_open(mount->dirname, O_RDONLY);
        if (fd == -1) {
884
            error_setg_errno(errp, errno, "failed to open %s", mount->dirname);
885 886 887 888 889 890 891 892 893 894 895 896
            goto error;
        }

        /* We try to cull filesytems we know won't work in advance, but other
         * filesytems may not implement fstrim for less obvious reasons.  These
         * will report EOPNOTSUPP; we simply ignore these errors.  Any other
         * error means an unexpected error, so return it in those cases.  In
         * some other cases ENOTTY will be reported (e.g. CD-ROMs).
         */
        ret = ioctl(fd, FITRIM, &r);
        if (ret == -1) {
            if (errno != ENOTTY && errno != EOPNOTSUPP) {
897
                error_setg_errno(errp, errno, "failed to trim %s",
898
                                 mount->dirname);
899 900 901 902 903 904 905 906 907 908 909 910 911
                close(fd);
                goto error;
            }
        }
        close(fd);
    }

error:
    free_fs_mount_list(&mounts);
}
#endif /* CONFIG_FSTRIM */


912 913 914 915 916
#define LINUX_SYS_STATE_FILE "/sys/power/state"
#define SUSPEND_SUPPORTED 0
#define SUSPEND_NOT_SUPPORTED 1

static void bios_supports_mode(const char *pmutils_bin, const char *pmutils_arg,
917
                               const char *sysfile_str, Error **errp)
918
{
919
    Error *local_err = NULL;
920
    char *pmutils_path;
921
    pid_t pid;
922
    int status;
923 924 925 926 927

    pmutils_path = g_find_program_in_path(pmutils_bin);

    pid = fork();
    if (!pid) {
928 929 930
        char buf[32]; /* hopefully big enough */
        ssize_t ret;
        int fd;
931 932 933 934 935 936

        setsid();
        reopen_fd_to_null(0);
        reopen_fd_to_null(1);
        reopen_fd_to_null(2);

937 938 939
        if (pmutils_path) {
            execle(pmutils_path, pmutils_bin, pmutils_arg, NULL, environ);
        }
940

941 942 943 944
        /*
         * If we get here either pm-utils is not installed or execle() has
         * failed. Let's try the manual method if the caller wants it.
         */
945

946 947 948
        if (!sysfile_str) {
            _exit(SUSPEND_NOT_SUPPORTED);
        }
949

950 951
        fd = open(LINUX_SYS_STATE_FILE, O_RDONLY);
        if (fd < 0) {
952 953 954
            _exit(SUSPEND_NOT_SUPPORTED);
        }

955 956 957
        ret = read(fd, buf, sizeof(buf)-1);
        if (ret <= 0) {
            _exit(SUSPEND_NOT_SUPPORTED);
958
        }
959
        buf[ret] = '\0';
960

961 962
        if (strstr(buf, sysfile_str)) {
            _exit(SUSPEND_SUPPORTED);
963 964
        }

965
        _exit(SUSPEND_NOT_SUPPORTED);
966
    } else if (pid < 0) {
967
        error_setg_errno(errp, errno, "failed to create child process");
968
        goto out;
969 970
    }

971
    ga_wait_child(pid, &status, &local_err);
972
    if (local_err) {
973
        error_propagate(errp, local_err);
974 975
        goto out;
    }
976

977
    if (!WIFEXITED(status)) {
978
        error_setg(errp, "child process has terminated abnormally");
979
        goto out;
980 981
    }

982 983 984 985
    switch (WEXITSTATUS(status)) {
    case SUSPEND_SUPPORTED:
        goto out;
    case SUSPEND_NOT_SUPPORTED:
986
        error_setg(errp,
987 988 989
                   "the requested suspend mode is not supported by the guest");
        goto out;
    default:
990
        error_setg(errp,
991 992 993
                   "the helper program '%s' returned an unexpected exit status"
                   " code (%d)", pmutils_path, WEXITSTATUS(status));
        goto out;
994 995
    }

996 997
out:
    g_free(pmutils_path);
998 999 1000
}

static void guest_suspend(const char *pmutils_bin, const char *sysfile_str,
1001
                          Error **errp)
1002
{
1003
    Error *local_err = NULL;
1004
    char *pmutils_path;
1005
    pid_t pid;
1006
    int status;
1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042

    pmutils_path = g_find_program_in_path(pmutils_bin);

    pid = fork();
    if (pid == 0) {
        /* child */
        int fd;

        setsid();
        reopen_fd_to_null(0);
        reopen_fd_to_null(1);
        reopen_fd_to_null(2);

        if (pmutils_path) {
            execle(pmutils_path, pmutils_bin, NULL, environ);
        }

        /*
         * If we get here either pm-utils is not installed or execle() has
         * failed. Let's try the manual method if the caller wants it.
         */

        if (!sysfile_str) {
            _exit(EXIT_FAILURE);
        }

        fd = open(LINUX_SYS_STATE_FILE, O_WRONLY);
        if (fd < 0) {
            _exit(EXIT_FAILURE);
        }

        if (write(fd, sysfile_str, strlen(sysfile_str)) < 0) {
            _exit(EXIT_FAILURE);
        }

        _exit(EXIT_SUCCESS);
1043
    } else if (pid < 0) {
1044
        error_setg_errno(errp, errno, "failed to create child process");
1045
        goto out;
1046 1047
    }

1048
    ga_wait_child(pid, &status, &local_err);
1049
    if (local_err) {
1050
        error_propagate(errp, local_err);
1051 1052
        goto out;
    }
1053

1054
    if (!WIFEXITED(status)) {
1055
        error_setg(errp, "child process has terminated abnormally");
1056
        goto out;
1057 1058
    }

1059
    if (WEXITSTATUS(status)) {
1060
        error_setg(errp, "child process has failed to suspend");
1061
        goto out;
1062
    }
1063

1064 1065
out:
    g_free(pmutils_path);
1066 1067
}

1068
void qmp_guest_suspend_disk(Error **errp)
1069
{
1070 1071 1072 1073 1074
    Error *local_err = NULL;

    bios_supports_mode("pm-is-supported", "--hibernate", "disk", &local_err);
    if (local_err) {
        error_propagate(errp, local_err);
1075 1076 1077
        return;
    }

1078
    guest_suspend("pm-hibernate", "disk", errp);
1079 1080
}

1081
void qmp_guest_suspend_ram(Error **errp)
L
Luiz Capitulino 已提交
1082
{
1083 1084 1085 1086 1087
    Error *local_err = NULL;

    bios_supports_mode("pm-is-supported", "--suspend", "mem", &local_err);
    if (local_err) {
        error_propagate(errp, local_err);
L
Luiz Capitulino 已提交
1088 1089 1090
        return;
    }

1091
    guest_suspend("pm-suspend", "mem", errp);
L
Luiz Capitulino 已提交
1092 1093
}

1094
void qmp_guest_suspend_hybrid(Error **errp)
1095
{
1096 1097 1098 1099 1100 1101
    Error *local_err = NULL;

    bios_supports_mode("pm-is-supported", "--suspend-hybrid", NULL,
                       &local_err);
    if (local_err) {
        error_propagate(errp, local_err);
1102 1103 1104
        return;
    }

1105
    guest_suspend("pm-suspend-hybrid", NULL, errp);
1106 1107
}

1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129
static GuestNetworkInterfaceList *
guest_find_interface(GuestNetworkInterfaceList *head,
                     const char *name)
{
    for (; head; head = head->next) {
        if (strcmp(head->value->name, name) == 0) {
            break;
        }
    }

    return head;
}

/*
 * Build information about guest interfaces
 */
GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp)
{
    GuestNetworkInterfaceList *head = NULL, *cur_item = NULL;
    struct ifaddrs *ifap, *ifa;

    if (getifaddrs(&ifap) < 0) {
1130
        error_setg_errno(errp, errno, "getifaddrs failed");
1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165
        goto error;
    }

    for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
        GuestNetworkInterfaceList *info;
        GuestIpAddressList **address_list = NULL, *address_item = NULL;
        char addr4[INET_ADDRSTRLEN];
        char addr6[INET6_ADDRSTRLEN];
        int sock;
        struct ifreq ifr;
        unsigned char *mac_addr;
        void *p;

        g_debug("Processing %s interface", ifa->ifa_name);

        info = guest_find_interface(head, ifa->ifa_name);

        if (!info) {
            info = g_malloc0(sizeof(*info));
            info->value = g_malloc0(sizeof(*info->value));
            info->value->name = g_strdup(ifa->ifa_name);

            if (!cur_item) {
                head = cur_item = info;
            } else {
                cur_item->next = info;
                cur_item = info;
            }
        }

        if (!info->value->has_hardware_address &&
            ifa->ifa_flags & SIOCGIFHWADDR) {
            /* we haven't obtained HW address yet */
            sock = socket(PF_INET, SOCK_STREAM, 0);
            if (sock == -1) {
1166
                error_setg_errno(errp, errno, "failed to create socket");
1167 1168 1169 1170
                goto error;
            }

            memset(&ifr, 0, sizeof(ifr));
1171
            pstrcpy(ifr.ifr_name, IF_NAMESIZE, info->value->name);
1172
            if (ioctl(sock, SIOCGIFHWADDR, &ifr) == -1) {
1173 1174 1175
                error_setg_errno(errp, errno,
                                 "failed to get MAC address of %s",
                                 ifa->ifa_name);
1176
                close(sock);
1177 1178 1179
                goto error;
            }

1180
            close(sock);
1181 1182
            mac_addr = (unsigned char *) &ifr.ifr_hwaddr.sa_data;

1183 1184 1185 1186 1187
            info->value->hardware_address =
                g_strdup_printf("%02x:%02x:%02x:%02x:%02x:%02x",
                                (int) mac_addr[0], (int) mac_addr[1],
                                (int) mac_addr[2], (int) mac_addr[3],
                                (int) mac_addr[4], (int) mac_addr[5]);
1188 1189 1190 1191 1192 1193 1194 1195 1196

            info->value->has_hardware_address = true;
        }

        if (ifa->ifa_addr &&
            ifa->ifa_addr->sa_family == AF_INET) {
            /* interface with IPv4 address */
            p = &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
            if (!inet_ntop(AF_INET, p, addr4, sizeof(addr4))) {
1197
                error_setg_errno(errp, errno, "inet_ntop failed");
1198 1199 1200
                goto error;
            }

1201 1202
            address_item = g_malloc0(sizeof(*address_item));
            address_item->value = g_malloc0(sizeof(*address_item->value));
1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216
            address_item->value->ip_address = g_strdup(addr4);
            address_item->value->ip_address_type = GUEST_IP_ADDRESS_TYPE_IPV4;

            if (ifa->ifa_netmask) {
                /* Count the number of set bits in netmask.
                 * This is safe as '1' and '0' cannot be shuffled in netmask. */
                p = &((struct sockaddr_in *)ifa->ifa_netmask)->sin_addr;
                address_item->value->prefix = ctpop32(((uint32_t *) p)[0]);
            }
        } else if (ifa->ifa_addr &&
                   ifa->ifa_addr->sa_family == AF_INET6) {
            /* interface with IPv6 address */
            p = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
            if (!inet_ntop(AF_INET6, p, addr6, sizeof(addr6))) {
1217
                error_setg_errno(errp, errno, "inet_ntop failed");
1218 1219 1220
                goto error;
            }

1221 1222
            address_item = g_malloc0(sizeof(*address_item));
            address_item->value = g_malloc0(sizeof(*address_item->value));
1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267
            address_item->value->ip_address = g_strdup(addr6);
            address_item->value->ip_address_type = GUEST_IP_ADDRESS_TYPE_IPV6;

            if (ifa->ifa_netmask) {
                /* Count the number of set bits in netmask.
                 * This is safe as '1' and '0' cannot be shuffled in netmask. */
                p = &((struct sockaddr_in6 *)ifa->ifa_netmask)->sin6_addr;
                address_item->value->prefix =
                    ctpop32(((uint32_t *) p)[0]) +
                    ctpop32(((uint32_t *) p)[1]) +
                    ctpop32(((uint32_t *) p)[2]) +
                    ctpop32(((uint32_t *) p)[3]);
            }
        }

        if (!address_item) {
            continue;
        }

        address_list = &info->value->ip_addresses;

        while (*address_list && (*address_list)->next) {
            address_list = &(*address_list)->next;
        }

        if (!*address_list) {
            *address_list = address_item;
        } else {
            (*address_list)->next = address_item;
        }

        info->value->has_ip_addresses = true;


    }

    freeifaddrs(ifap);
    return head;

error:
    freeifaddrs(ifap);
    qapi_free_GuestNetworkInterfaceList(head);
    return NULL;
}

1268
#define SYSCONF_EXACT(name, errp) sysconf_exact((name), #name, (errp))
1269

1270
static long sysconf_exact(int name, const char *name_str, Error **errp)
1271 1272 1273 1274 1275 1276 1277
{
    long ret;

    errno = 0;
    ret = sysconf(name);
    if (ret == -1) {
        if (errno == 0) {
1278
            error_setg(errp, "sysconf(%s): value indefinite", name_str);
1279
        } else {
1280
            error_setg_errno(errp, errno, "sysconf(%s)", name_str);
1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397
        }
    }
    return ret;
}

/* Transfer online/offline status between @vcpu and the guest system.
 *
 * On input either @errp or *@errp must be NULL.
 *
 * In system-to-@vcpu direction, the following @vcpu fields are accessed:
 * - R: vcpu->logical_id
 * - W: vcpu->online
 * - W: vcpu->can_offline
 *
 * In @vcpu-to-system direction, the following @vcpu fields are accessed:
 * - R: vcpu->logical_id
 * - R: vcpu->online
 *
 * Written members remain unmodified on error.
 */
static void transfer_vcpu(GuestLogicalProcessor *vcpu, bool sys2vcpu,
                          Error **errp)
{
    char *dirpath;
    int dirfd;

    dirpath = g_strdup_printf("/sys/devices/system/cpu/cpu%" PRId64 "/",
                              vcpu->logical_id);
    dirfd = open(dirpath, O_RDONLY | O_DIRECTORY);
    if (dirfd == -1) {
        error_setg_errno(errp, errno, "open(\"%s\")", dirpath);
    } else {
        static const char fn[] = "online";
        int fd;
        int res;

        fd = openat(dirfd, fn, sys2vcpu ? O_RDONLY : O_RDWR);
        if (fd == -1) {
            if (errno != ENOENT) {
                error_setg_errno(errp, errno, "open(\"%s/%s\")", dirpath, fn);
            } else if (sys2vcpu) {
                vcpu->online = true;
                vcpu->can_offline = false;
            } else if (!vcpu->online) {
                error_setg(errp, "logical processor #%" PRId64 " can't be "
                           "offlined", vcpu->logical_id);
            } /* otherwise pretend successful re-onlining */
        } else {
            unsigned char status;

            res = pread(fd, &status, 1, 0);
            if (res == -1) {
                error_setg_errno(errp, errno, "pread(\"%s/%s\")", dirpath, fn);
            } else if (res == 0) {
                error_setg(errp, "pread(\"%s/%s\"): unexpected EOF", dirpath,
                           fn);
            } else if (sys2vcpu) {
                vcpu->online = (status != '0');
                vcpu->can_offline = true;
            } else if (vcpu->online != (status != '0')) {
                status = '0' + vcpu->online;
                if (pwrite(fd, &status, 1, 0) == -1) {
                    error_setg_errno(errp, errno, "pwrite(\"%s/%s\")", dirpath,
                                     fn);
                }
            } /* otherwise pretend successful re-(on|off)-lining */

            res = close(fd);
            g_assert(res == 0);
        }

        res = close(dirfd);
        g_assert(res == 0);
    }

    g_free(dirpath);
}

GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp)
{
    int64_t current;
    GuestLogicalProcessorList *head, **link;
    long sc_max;
    Error *local_err = NULL;

    current = 0;
    head = NULL;
    link = &head;
    sc_max = SYSCONF_EXACT(_SC_NPROCESSORS_CONF, &local_err);

    while (local_err == NULL && current < sc_max) {
        GuestLogicalProcessor *vcpu;
        GuestLogicalProcessorList *entry;

        vcpu = g_malloc0(sizeof *vcpu);
        vcpu->logical_id = current++;
        vcpu->has_can_offline = true; /* lolspeak ftw */
        transfer_vcpu(vcpu, true, &local_err);

        entry = g_malloc0(sizeof *entry);
        entry->value = vcpu;

        *link = entry;
        link = &entry->next;
    }

    if (local_err == NULL) {
        /* there's no guest with zero VCPUs */
        g_assert(head != NULL);
        return head;
    }

    qapi_free_GuestLogicalProcessorList(head);
    error_propagate(errp, local_err);
    return NULL;
}

1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423
int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp)
{
    int64_t processed;
    Error *local_err = NULL;

    processed = 0;
    while (vcpus != NULL) {
        transfer_vcpu(vcpus->value, false, &local_err);
        if (local_err != NULL) {
            break;
        }
        ++processed;
        vcpus = vcpus->next;
    }

    if (local_err != NULL) {
        if (processed == 0) {
            error_propagate(errp, local_err);
        } else {
            error_free(local_err);
        }
    }

    return processed;
}

1424 1425
#else /* defined(__linux__) */

1426
void qmp_guest_suspend_disk(Error **errp)
1427
{
1428
    error_set(errp, QERR_UNSUPPORTED);
1429 1430
}

1431
void qmp_guest_suspend_ram(Error **errp)
1432
{
1433
    error_set(errp, QERR_UNSUPPORTED);
1434 1435
}

1436
void qmp_guest_suspend_hybrid(Error **errp)
1437
{
1438
    error_set(errp, QERR_UNSUPPORTED);
1439 1440
}

1441
GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp)
1442
{
1443 1444
    error_set(errp, QERR_UNSUPPORTED);
    return NULL;
1445 1446
}

1447 1448 1449 1450 1451 1452
GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp)
{
    error_set(errp, QERR_UNSUPPORTED);
    return NULL;
}

1453 1454 1455 1456 1457 1458
int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp)
{
    error_set(errp, QERR_UNSUPPORTED);
    return -1;
}

1459 1460 1461 1462
#endif

#if !defined(CONFIG_FSFREEZE)

1463
GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **errp)
1464
{
1465
    error_set(errp, QERR_UNSUPPORTED);
1466 1467

    return 0;
1468 1469
}

1470
int64_t qmp_guest_fsfreeze_freeze(Error **errp)
1471
{
1472
    error_set(errp, QERR_UNSUPPORTED);
1473 1474

    return 0;
1475 1476
}

1477
int64_t qmp_guest_fsfreeze_thaw(Error **errp)
1478
{
1479
    error_set(errp, QERR_UNSUPPORTED);
1480 1481

    return 0;
1482
}
1483 1484 1485
#endif /* CONFIG_FSFREEZE */

#if !defined(CONFIG_FSTRIM)
1486
void qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp)
1487
{
1488
    error_set(errp, QERR_UNSUPPORTED);
1489
}
1490 1491
#endif

1492 1493 1494
/* register init/cleanup routines for stateful command groups */
void ga_command_state_init(GAState *s, GACommandState *cs)
{
1495
#if defined(CONFIG_FSFREEZE)
1496
    ga_command_state_add(cs, NULL, guest_fsfreeze_cleanup);
1497
#endif
1498 1499
    ga_command_state_add(cs, guest_file_init, NULL);
}