qemu/qga/commands-win32.c
<<
>>
Prefs
   1/*
   2 * QEMU Guest Agent win32-specific command implementations
   3 *
   4 * Copyright IBM Corp. 2012
   5 *
   6 * Authors:
   7 *  Michael Roth      <mdroth@linux.vnet.ibm.com>
   8 *  Gal Hammer        <ghammer@redhat.com>
   9 *
  10 * This work is licensed under the terms of the GNU GPL, version 2 or later.
  11 * See the COPYING file in the top-level directory.
  12 */
  13
  14#include <glib.h>
  15#include <wtypes.h>
  16#include <powrprof.h>
  17#include "qga/guest-agent-core.h"
  18#include "qga-qmp-commands.h"
  19#include "qapi/qmp/qerror.h"
  20
  21#ifndef SHTDN_REASON_FLAG_PLANNED
  22#define SHTDN_REASON_FLAG_PLANNED 0x80000000
  23#endif
  24
  25static void acquire_privilege(const char *name, Error **err)
  26{
  27    HANDLE token;
  28    TOKEN_PRIVILEGES priv;
  29    Error *local_err = NULL;
  30
  31    if (error_is_set(err)) {
  32        return;
  33    }
  34
  35    if (OpenProcessToken(GetCurrentProcess(),
  36        TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, &token))
  37    {
  38        if (!LookupPrivilegeValue(NULL, name, &priv.Privileges[0].Luid)) {
  39            error_set(&local_err, QERR_QGA_COMMAND_FAILED,
  40                      "no luid for requested privilege");
  41            goto out;
  42        }
  43
  44        priv.PrivilegeCount = 1;
  45        priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
  46
  47        if (!AdjustTokenPrivileges(token, FALSE, &priv, 0, NULL, 0)) {
  48            error_set(&local_err, QERR_QGA_COMMAND_FAILED,
  49                      "unable to acquire requested privilege");
  50            goto out;
  51        }
  52
  53        CloseHandle(token);
  54    } else {
  55        error_set(&local_err, QERR_QGA_COMMAND_FAILED,
  56                  "failed to open privilege token");
  57    }
  58
  59out:
  60    if (local_err) {
  61        error_propagate(err, local_err);
  62    }
  63}
  64
  65static void execute_async(DWORD WINAPI (*func)(LPVOID), LPVOID opaque, Error **err)
  66{
  67    Error *local_err = NULL;
  68
  69    if (error_is_set(err)) {
  70        return;
  71    }
  72    HANDLE thread = CreateThread(NULL, 0, func, opaque, 0, NULL);
  73    if (!thread) {
  74        error_set(&local_err, QERR_QGA_COMMAND_FAILED,
  75                  "failed to dispatch asynchronous command");
  76        error_propagate(err, local_err);
  77    }
  78}
  79
  80void qmp_guest_shutdown(bool has_mode, const char *mode, Error **err)
  81{
  82    UINT shutdown_flag = EWX_FORCE;
  83
  84    slog("guest-shutdown called, mode: %s", mode);
  85
  86    if (!has_mode || strcmp(mode, "powerdown") == 0) {
  87        shutdown_flag |= EWX_POWEROFF;
  88    } else if (strcmp(mode, "halt") == 0) {
  89        shutdown_flag |= EWX_SHUTDOWN;
  90    } else if (strcmp(mode, "reboot") == 0) {
  91        shutdown_flag |= EWX_REBOOT;
  92    } else {
  93        error_set(err, QERR_INVALID_PARAMETER_VALUE, "mode",
  94                  "halt|powerdown|reboot");
  95        return;
  96    }
  97
  98    /* Request a shutdown privilege, but try to shut down the system
  99       anyway. */
 100    acquire_privilege(SE_SHUTDOWN_NAME, err);
 101    if (error_is_set(err)) {
 102        return;
 103    }
 104
 105    if (!ExitWindowsEx(shutdown_flag, SHTDN_REASON_FLAG_PLANNED)) {
 106        slog("guest-shutdown failed: %d", GetLastError());
 107        error_set(err, QERR_UNDEFINED_ERROR);
 108    }
 109}
 110
 111int64_t qmp_guest_file_open(const char *path, bool has_mode, const char *mode, Error **err)
 112{
 113    error_set(err, QERR_UNSUPPORTED);
 114    return 0;
 115}
 116
 117void qmp_guest_file_close(int64_t handle, Error **err)
 118{
 119    error_set(err, QERR_UNSUPPORTED);
 120}
 121
 122GuestFileRead *qmp_guest_file_read(int64_t handle, bool has_count,
 123                                   int64_t count, Error **err)
 124{
 125    error_set(err, QERR_UNSUPPORTED);
 126    return 0;
 127}
 128
 129GuestFileWrite *qmp_guest_file_write(int64_t handle, const char *buf_b64,
 130                                     bool has_count, int64_t count, Error **err)
 131{
 132    error_set(err, QERR_UNSUPPORTED);
 133    return 0;
 134}
 135
 136GuestFileSeek *qmp_guest_file_seek(int64_t handle, int64_t offset,
 137                                   int64_t whence, Error **err)
 138{
 139    error_set(err, QERR_UNSUPPORTED);
 140    return 0;
 141}
 142
 143void qmp_guest_file_flush(int64_t handle, Error **err)
 144{
 145    error_set(err, QERR_UNSUPPORTED);
 146}
 147
 148/*
 149 * Return status of freeze/thaw
 150 */
 151GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **err)
 152{
 153    error_set(err, QERR_UNSUPPORTED);
 154    return 0;
 155}
 156
 157/*
 158 * Walk list of mounted file systems in the guest, and freeze the ones which
 159 * are real local file systems.
 160 */
 161int64_t qmp_guest_fsfreeze_freeze(Error **err)
 162{
 163    error_set(err, QERR_UNSUPPORTED);
 164    return 0;
 165}
 166
 167/*
 168 * Walk list of frozen file systems in the guest, and thaw them.
 169 */
 170int64_t qmp_guest_fsfreeze_thaw(Error **err)
 171{
 172    error_set(err, QERR_UNSUPPORTED);
 173    return 0;
 174}
 175
 176/*
 177 * Walk list of mounted file systems in the guest, and discard unused
 178 * areas.
 179 */
 180void qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **err)
 181{
 182    error_set(err, QERR_UNSUPPORTED);
 183}
 184
 185typedef enum {
 186    GUEST_SUSPEND_MODE_DISK,
 187    GUEST_SUSPEND_MODE_RAM
 188} GuestSuspendMode;
 189
 190static void check_suspend_mode(GuestSuspendMode mode, Error **err)
 191{
 192    SYSTEM_POWER_CAPABILITIES sys_pwr_caps;
 193    Error *local_err = NULL;
 194
 195    if (error_is_set(err)) {
 196        return;
 197    }
 198    ZeroMemory(&sys_pwr_caps, sizeof(sys_pwr_caps));
 199    if (!GetPwrCapabilities(&sys_pwr_caps)) {
 200        error_set(&local_err, QERR_QGA_COMMAND_FAILED,
 201                  "failed to determine guest suspend capabilities");
 202        goto out;
 203    }
 204
 205    switch (mode) {
 206    case GUEST_SUSPEND_MODE_DISK:
 207        if (!sys_pwr_caps.SystemS4) {
 208            error_set(&local_err, QERR_QGA_COMMAND_FAILED,
 209                      "suspend-to-disk not supported by OS");
 210        }
 211        break;
 212    case GUEST_SUSPEND_MODE_RAM:
 213        if (!sys_pwr_caps.SystemS3) {
 214            error_set(&local_err, QERR_QGA_COMMAND_FAILED,
 215                      "suspend-to-ram not supported by OS");
 216        }
 217        break;
 218    default:
 219        error_set(&local_err, QERR_INVALID_PARAMETER_VALUE, "mode",
 220                  "GuestSuspendMode");
 221    }
 222
 223out:
 224    if (local_err) {
 225        error_propagate(err, local_err);
 226    }
 227}
 228
 229static DWORD WINAPI do_suspend(LPVOID opaque)
 230{
 231    GuestSuspendMode *mode = opaque;
 232    DWORD ret = 0;
 233
 234    if (!SetSuspendState(*mode == GUEST_SUSPEND_MODE_DISK, TRUE, TRUE)) {
 235        slog("failed to suspend guest, %s", GetLastError());
 236        ret = -1;
 237    }
 238    g_free(mode);
 239    return ret;
 240}
 241
 242void qmp_guest_suspend_disk(Error **err)
 243{
 244    GuestSuspendMode *mode = g_malloc(sizeof(GuestSuspendMode));
 245
 246    *mode = GUEST_SUSPEND_MODE_DISK;
 247    check_suspend_mode(*mode, err);
 248    acquire_privilege(SE_SHUTDOWN_NAME, err);
 249    execute_async(do_suspend, mode, err);
 250
 251    if (error_is_set(err)) {
 252        g_free(mode);
 253    }
 254}
 255
 256void qmp_guest_suspend_ram(Error **err)
 257{
 258    GuestSuspendMode *mode = g_malloc(sizeof(GuestSuspendMode));
 259
 260    *mode = GUEST_SUSPEND_MODE_RAM;
 261    check_suspend_mode(*mode, err);
 262    acquire_privilege(SE_SHUTDOWN_NAME, err);
 263    execute_async(do_suspend, mode, err);
 264
 265    if (error_is_set(err)) {
 266        g_free(mode);
 267    }
 268}
 269
 270void qmp_guest_suspend_hybrid(Error **err)
 271{
 272    error_set(err, QERR_UNSUPPORTED);
 273}
 274
 275GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **err)
 276{
 277    error_set(err, QERR_UNSUPPORTED);
 278    return NULL;
 279}
 280
 281/* register init/cleanup routines for stateful command groups */
 282void ga_command_state_init(GAState *s, GACommandState *cs)
 283{
 284}
 285