Statistics
| Branch: | Revision:

root / qga / commands-win32.c @ d91a68a7

History | View | Annotate | Download (10.6 kB)

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/vss-win32.h"
19
#include "qga-qmp-commands.h"
20
#include "qapi/qmp/qerror.h"
21

    
22
#ifndef SHTDN_REASON_FLAG_PLANNED
23
#define SHTDN_REASON_FLAG_PLANNED 0x80000000
24
#endif
25

    
26
/* multiple of 100 nanoseconds elapsed between windows baseline
27
 *    (1/1/1601) and Unix Epoch (1/1/1970), accounting for leap years */
28
#define W32_FT_OFFSET (10000000ULL * 60 * 60 * 24 * \
29
                       (365 * (1970 - 1601) +       \
30
                        (1970 - 1601) / 4 - 3))
31

    
32
static void acquire_privilege(const char *name, Error **err)
33
{
34
    HANDLE token;
35
    TOKEN_PRIVILEGES priv;
36
    Error *local_err = NULL;
37

    
38
    if (error_is_set(err)) {
39
        return;
40
    }
41

    
42
    if (OpenProcessToken(GetCurrentProcess(),
43
        TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, &token))
44
    {
45
        if (!LookupPrivilegeValue(NULL, name, &priv.Privileges[0].Luid)) {
46
            error_set(&local_err, QERR_QGA_COMMAND_FAILED,
47
                      "no luid for requested privilege");
48
            goto out;
49
        }
50

    
51
        priv.PrivilegeCount = 1;
52
        priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
53

    
54
        if (!AdjustTokenPrivileges(token, FALSE, &priv, 0, NULL, 0)) {
55
            error_set(&local_err, QERR_QGA_COMMAND_FAILED,
56
                      "unable to acquire requested privilege");
57
            goto out;
58
        }
59

    
60
        CloseHandle(token);
61
    } else {
62
        error_set(&local_err, QERR_QGA_COMMAND_FAILED,
63
                  "failed to open privilege token");
64
    }
65

    
66
out:
67
    if (local_err) {
68
        error_propagate(err, local_err);
69
    }
70
}
71

    
72
static void execute_async(DWORD WINAPI (*func)(LPVOID), LPVOID opaque, Error **err)
73
{
74
    Error *local_err = NULL;
75

    
76
    if (error_is_set(err)) {
77
        return;
78
    }
79
    HANDLE thread = CreateThread(NULL, 0, func, opaque, 0, NULL);
80
    if (!thread) {
81
        error_set(&local_err, QERR_QGA_COMMAND_FAILED,
82
                  "failed to dispatch asynchronous command");
83
        error_propagate(err, local_err);
84
    }
85
}
86

    
87
void qmp_guest_shutdown(bool has_mode, const char *mode, Error **err)
88
{
89
    UINT shutdown_flag = EWX_FORCE;
90

    
91
    slog("guest-shutdown called, mode: %s", mode);
92

    
93
    if (!has_mode || strcmp(mode, "powerdown") == 0) {
94
        shutdown_flag |= EWX_POWEROFF;
95
    } else if (strcmp(mode, "halt") == 0) {
96
        shutdown_flag |= EWX_SHUTDOWN;
97
    } else if (strcmp(mode, "reboot") == 0) {
98
        shutdown_flag |= EWX_REBOOT;
99
    } else {
100
        error_set(err, QERR_INVALID_PARAMETER_VALUE, "mode",
101
                  "halt|powerdown|reboot");
102
        return;
103
    }
104

    
105
    /* Request a shutdown privilege, but try to shut down the system
106
       anyway. */
107
    acquire_privilege(SE_SHUTDOWN_NAME, err);
108
    if (error_is_set(err)) {
109
        return;
110
    }
111

    
112
    if (!ExitWindowsEx(shutdown_flag, SHTDN_REASON_FLAG_PLANNED)) {
113
        slog("guest-shutdown failed: %lu", GetLastError());
114
        error_set(err, QERR_UNDEFINED_ERROR);
115
    }
116
}
117

    
118
int64_t qmp_guest_file_open(const char *path, bool has_mode, const char *mode, Error **err)
119
{
120
    error_set(err, QERR_UNSUPPORTED);
121
    return 0;
122
}
123

    
124
void qmp_guest_file_close(int64_t handle, Error **err)
125
{
126
    error_set(err, QERR_UNSUPPORTED);
127
}
128

    
129
GuestFileRead *qmp_guest_file_read(int64_t handle, bool has_count,
130
                                   int64_t count, Error **err)
131
{
132
    error_set(err, QERR_UNSUPPORTED);
133
    return 0;
134
}
135

    
136
GuestFileWrite *qmp_guest_file_write(int64_t handle, const char *buf_b64,
137
                                     bool has_count, int64_t count, Error **err)
138
{
139
    error_set(err, QERR_UNSUPPORTED);
140
    return 0;
141
}
142

    
143
GuestFileSeek *qmp_guest_file_seek(int64_t handle, int64_t offset,
144
                                   int64_t whence, Error **err)
145
{
146
    error_set(err, QERR_UNSUPPORTED);
147
    return 0;
148
}
149

    
150
void qmp_guest_file_flush(int64_t handle, Error **err)
151
{
152
    error_set(err, QERR_UNSUPPORTED);
153
}
154

    
155
/*
156
 * Return status of freeze/thaw
157
 */
158
GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **err)
159
{
160
    if (!vss_initialized()) {
161
        error_set(err, QERR_UNSUPPORTED);
162
        return 0;
163
    }
164

    
165
    if (ga_is_frozen(ga_state)) {
166
        return GUEST_FSFREEZE_STATUS_FROZEN;
167
    }
168

    
169
    return GUEST_FSFREEZE_STATUS_THAWED;
170
}
171

    
172
/*
173
 * Freeze local file systems using Volume Shadow-copy Service.
174
 * The frozen state is limited for up to 10 seconds by VSS.
175
 */
176
int64_t qmp_guest_fsfreeze_freeze(Error **err)
177
{
178
    int i;
179
    Error *local_err = NULL;
180

    
181
    if (!vss_initialized()) {
182
        error_set(err, QERR_UNSUPPORTED);
183
        return 0;
184
    }
185

    
186
    slog("guest-fsfreeze called");
187

    
188
    /* cannot risk guest agent blocking itself on a write in this state */
189
    ga_set_frozen(ga_state);
190

    
191
    qga_vss_fsfreeze(&i, err, true);
192
    if (error_is_set(err)) {
193
        goto error;
194
    }
195

    
196
    return i;
197

    
198
error:
199
    qmp_guest_fsfreeze_thaw(&local_err);
200
    if (local_err) {
201
        g_debug("cleanup thaw: %s", error_get_pretty(local_err));
202
        error_free(local_err);
203
    }
204
    return 0;
205
}
206

    
207
/*
208
 * Thaw local file systems using Volume Shadow-copy Service.
209
 */
210
int64_t qmp_guest_fsfreeze_thaw(Error **err)
211
{
212
    int i;
213

    
214
    if (!vss_initialized()) {
215
        error_set(err, QERR_UNSUPPORTED);
216
        return 0;
217
    }
218

    
219
    qga_vss_fsfreeze(&i, err, false);
220

    
221
    ga_unset_frozen(ga_state);
222
    return i;
223
}
224

    
225
static void guest_fsfreeze_cleanup(void)
226
{
227
    Error *err = NULL;
228

    
229
    if (!vss_initialized()) {
230
        return;
231
    }
232

    
233
    if (ga_is_frozen(ga_state) == GUEST_FSFREEZE_STATUS_FROZEN) {
234
        qmp_guest_fsfreeze_thaw(&err);
235
        if (err) {
236
            slog("failed to clean up frozen filesystems: %s",
237
                 error_get_pretty(err));
238
            error_free(err);
239
        }
240
    }
241

    
242
    vss_deinit(true);
243
}
244

    
245
/*
246
 * Walk list of mounted file systems in the guest, and discard unused
247
 * areas.
248
 */
249
void qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **err)
250
{
251
    error_set(err, QERR_UNSUPPORTED);
252
}
253

    
254
typedef enum {
255
    GUEST_SUSPEND_MODE_DISK,
256
    GUEST_SUSPEND_MODE_RAM
257
} GuestSuspendMode;
258

    
259
static void check_suspend_mode(GuestSuspendMode mode, Error **err)
260
{
261
    SYSTEM_POWER_CAPABILITIES sys_pwr_caps;
262
    Error *local_err = NULL;
263

    
264
    if (error_is_set(err)) {
265
        return;
266
    }
267
    ZeroMemory(&sys_pwr_caps, sizeof(sys_pwr_caps));
268
    if (!GetPwrCapabilities(&sys_pwr_caps)) {
269
        error_set(&local_err, QERR_QGA_COMMAND_FAILED,
270
                  "failed to determine guest suspend capabilities");
271
        goto out;
272
    }
273

    
274
    switch (mode) {
275
    case GUEST_SUSPEND_MODE_DISK:
276
        if (!sys_pwr_caps.SystemS4) {
277
            error_set(&local_err, QERR_QGA_COMMAND_FAILED,
278
                      "suspend-to-disk not supported by OS");
279
        }
280
        break;
281
    case GUEST_SUSPEND_MODE_RAM:
282
        if (!sys_pwr_caps.SystemS3) {
283
            error_set(&local_err, QERR_QGA_COMMAND_FAILED,
284
                      "suspend-to-ram not supported by OS");
285
        }
286
        break;
287
    default:
288
        error_set(&local_err, QERR_INVALID_PARAMETER_VALUE, "mode",
289
                  "GuestSuspendMode");
290
    }
291

    
292
out:
293
    if (local_err) {
294
        error_propagate(err, local_err);
295
    }
296
}
297

    
298
static DWORD WINAPI do_suspend(LPVOID opaque)
299
{
300
    GuestSuspendMode *mode = opaque;
301
    DWORD ret = 0;
302

    
303
    if (!SetSuspendState(*mode == GUEST_SUSPEND_MODE_DISK, TRUE, TRUE)) {
304
        slog("failed to suspend guest, %lu", GetLastError());
305
        ret = -1;
306
    }
307
    g_free(mode);
308
    return ret;
309
}
310

    
311
void qmp_guest_suspend_disk(Error **err)
312
{
313
    GuestSuspendMode *mode = g_malloc(sizeof(GuestSuspendMode));
314

    
315
    *mode = GUEST_SUSPEND_MODE_DISK;
316
    check_suspend_mode(*mode, err);
317
    acquire_privilege(SE_SHUTDOWN_NAME, err);
318
    execute_async(do_suspend, mode, err);
319

    
320
    if (error_is_set(err)) {
321
        g_free(mode);
322
    }
323
}
324

    
325
void qmp_guest_suspend_ram(Error **err)
326
{
327
    GuestSuspendMode *mode = g_malloc(sizeof(GuestSuspendMode));
328

    
329
    *mode = GUEST_SUSPEND_MODE_RAM;
330
    check_suspend_mode(*mode, err);
331
    acquire_privilege(SE_SHUTDOWN_NAME, err);
332
    execute_async(do_suspend, mode, err);
333

    
334
    if (error_is_set(err)) {
335
        g_free(mode);
336
    }
337
}
338

    
339
void qmp_guest_suspend_hybrid(Error **err)
340
{
341
    error_set(err, QERR_UNSUPPORTED);
342
}
343

    
344
GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **err)
345
{
346
    error_set(err, QERR_UNSUPPORTED);
347
    return NULL;
348
}
349

    
350
int64_t qmp_guest_get_time(Error **errp)
351
{
352
    SYSTEMTIME ts = {0};
353
    int64_t time_ns;
354
    FILETIME tf;
355

    
356
    GetSystemTime(&ts);
357
    if (ts.wYear < 1601 || ts.wYear > 30827) {
358
        error_setg(errp, "Failed to get time");
359
        return -1;
360
    }
361

    
362
    if (!SystemTimeToFileTime(&ts, &tf)) {
363
        error_setg(errp, "Failed to convert system time: %d", (int)GetLastError());
364
        return -1;
365
    }
366

    
367
    time_ns = ((((int64_t)tf.dwHighDateTime << 32) | tf.dwLowDateTime)
368
                - W32_FT_OFFSET) * 100;
369

    
370
    return time_ns;
371
}
372

    
373
void qmp_guest_set_time(bool has_time, int64_t time_ns, Error **errp)
374
{
375
    SYSTEMTIME ts;
376
    FILETIME tf;
377
    LONGLONG time;
378

    
379
    if (has_time) {
380
        /* Okay, user passed a time to set. Validate it. */
381
        if (time_ns < 0 || time_ns / 100 > INT64_MAX - W32_FT_OFFSET) {
382
            error_setg(errp, "Time %" PRId64 "is invalid", time_ns);
383
            return;
384
        }
385

    
386
        time = time_ns / 100 + W32_FT_OFFSET;
387

    
388
        tf.dwLowDateTime = (DWORD) time;
389
        tf.dwHighDateTime = (DWORD) (time >> 32);
390

    
391
        if (!FileTimeToSystemTime(&tf, &ts)) {
392
            error_setg(errp, "Failed to convert system time %d",
393
                       (int)GetLastError());
394
            return;
395
        }
396
    } else {
397
        /* Otherwise read the time from RTC which contains the correct value.
398
         * Hopefully. */
399
        GetSystemTime(&ts);
400
        if (ts.wYear < 1601 || ts.wYear > 30827) {
401
            error_setg(errp, "Failed to get time");
402
            return;
403
        }
404
    }
405

    
406
    acquire_privilege(SE_SYSTEMTIME_NAME, errp);
407
    if (error_is_set(errp)) {
408
        return;
409
    }
410

    
411
    if (!SetSystemTime(&ts)) {
412
        error_setg(errp, "Failed to set time to guest: %d", (int)GetLastError());
413
        return;
414
    }
415
}
416

    
417
GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp)
418
{
419
    error_set(errp, QERR_UNSUPPORTED);
420
    return NULL;
421
}
422

    
423
int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp)
424
{
425
    error_set(errp, QERR_UNSUPPORTED);
426
    return -1;
427
}
428

    
429
/* register init/cleanup routines for stateful command groups */
430
void ga_command_state_init(GAState *s, GACommandState *cs)
431
{
432
    if (vss_init(true)) {
433
        ga_command_state_add(cs, NULL, guest_fsfreeze_cleanup);
434
    }
435
}