]> git.proxmox.com Git - mirror_qemu.git/blame - qga/commands-win32.c
qga: add win32 library iphlpapi
[mirror_qemu.git] / qga / commands-win32.c
CommitLineData
d8ca685a
MR
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>
aa59637e 8 * Gal Hammer <ghammer@redhat.com>
d8ca685a
MR
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>
aa59637e
GH
15#include <wtypes.h>
16#include <powrprof.h>
fa193594
OK
17#include <stdio.h>
18#include <string.h>
d8ca685a 19#include "qga/guest-agent-core.h"
64c00317 20#include "qga/vss-win32.h"
d8ca685a 21#include "qga-qmp-commands.h"
7b1b5d19 22#include "qapi/qmp/qerror.h"
fa193594 23#include "qemu/queue.h"
d8ca685a 24
546b60d0
MR
25#ifndef SHTDN_REASON_FLAG_PLANNED
26#define SHTDN_REASON_FLAG_PLANNED 0x80000000
27#endif
28
3f2a6087
LL
29/* multiple of 100 nanoseconds elapsed between windows baseline
30 * (1/1/1601) and Unix Epoch (1/1/1970), accounting for leap years */
31#define W32_FT_OFFSET (10000000ULL * 60 * 60 * 24 * \
32 (365 * (1970 - 1601) + \
33 (1970 - 1601) / 4 - 3))
34
fa193594
OK
35#define INVALID_SET_FILE_POINTER ((DWORD)-1)
36
37typedef struct GuestFileHandle {
38 int64_t id;
39 HANDLE fh;
40 QTAILQ_ENTRY(GuestFileHandle) next;
41} GuestFileHandle;
42
43static struct {
44 QTAILQ_HEAD(, GuestFileHandle) filehandles;
45} guest_file_state;
46
47
48typedef struct OpenFlags {
49 const char *forms;
50 DWORD desired_access;
51 DWORD creation_disposition;
52} OpenFlags;
53static OpenFlags guest_file_open_modes[] = {
54 {"r", GENERIC_READ, OPEN_EXISTING},
55 {"rb", GENERIC_READ, OPEN_EXISTING},
56 {"w", GENERIC_WRITE, CREATE_ALWAYS},
57 {"wb", GENERIC_WRITE, CREATE_ALWAYS},
58 {"a", GENERIC_WRITE, OPEN_ALWAYS },
59 {"r+", GENERIC_WRITE|GENERIC_READ, OPEN_EXISTING},
60 {"rb+", GENERIC_WRITE|GENERIC_READ, OPEN_EXISTING},
61 {"r+b", GENERIC_WRITE|GENERIC_READ, OPEN_EXISTING},
62 {"w+", GENERIC_WRITE|GENERIC_READ, CREATE_ALWAYS},
63 {"wb+", GENERIC_WRITE|GENERIC_READ, CREATE_ALWAYS},
64 {"w+b", GENERIC_WRITE|GENERIC_READ, CREATE_ALWAYS},
65 {"a+", GENERIC_WRITE|GENERIC_READ, OPEN_ALWAYS },
66 {"ab+", GENERIC_WRITE|GENERIC_READ, OPEN_ALWAYS },
67 {"a+b", GENERIC_WRITE|GENERIC_READ, OPEN_ALWAYS }
68};
69
70static OpenFlags *find_open_flag(const char *mode_str)
71{
72 int mode;
73 Error **errp = NULL;
74
75 for (mode = 0; mode < ARRAY_SIZE(guest_file_open_modes); ++mode) {
76 OpenFlags *flags = guest_file_open_modes + mode;
77
78 if (strcmp(flags->forms, mode_str) == 0) {
79 return flags;
80 }
81 }
82
83 error_setg(errp, "invalid file open mode '%s'", mode_str);
84 return NULL;
85}
86
87static int64_t guest_file_handle_add(HANDLE fh, Error **errp)
88{
89 GuestFileHandle *gfh;
90 int64_t handle;
91
92 handle = ga_get_fd_handle(ga_state, errp);
93 if (handle < 0) {
94 return -1;
95 }
96 gfh = g_malloc0(sizeof(GuestFileHandle));
97 gfh->id = handle;
98 gfh->fh = fh;
99 QTAILQ_INSERT_TAIL(&guest_file_state.filehandles, gfh, next);
100
101 return handle;
102}
103
104static GuestFileHandle *guest_file_handle_find(int64_t id, Error **errp)
105{
106 GuestFileHandle *gfh;
107 QTAILQ_FOREACH(gfh, &guest_file_state.filehandles, next) {
108 if (gfh->id == id) {
109 return gfh;
110 }
111 }
112 error_setg(errp, "handle '%" PRId64 "' has not been found", id);
113 return NULL;
114}
115
116int64_t qmp_guest_file_open(const char *path, bool has_mode,
117 const char *mode, Error **errp)
118{
119 int64_t fd;
120 HANDLE fh;
121 HANDLE templ_file = NULL;
122 DWORD share_mode = FILE_SHARE_READ;
123 DWORD flags_and_attr = FILE_ATTRIBUTE_NORMAL;
124 LPSECURITY_ATTRIBUTES sa_attr = NULL;
125 OpenFlags *guest_flags;
126
127 if (!has_mode) {
128 mode = "r";
129 }
130 slog("guest-file-open called, filepath: %s, mode: %s", path, mode);
131 guest_flags = find_open_flag(mode);
132 if (guest_flags == NULL) {
133 error_setg(errp, "invalid file open mode");
134 return -1;
135 }
136
137 fh = CreateFile(path, guest_flags->desired_access, share_mode, sa_attr,
138 guest_flags->creation_disposition, flags_and_attr,
139 templ_file);
140 if (fh == INVALID_HANDLE_VALUE) {
141 error_setg_win32(errp, GetLastError(), "failed to open file '%s'",
142 path);
143 return -1;
144 }
145
146 fd = guest_file_handle_add(fh, errp);
147 if (fd < 0) {
148 CloseHandle(&fh);
149 error_setg(errp, "failed to add handle to qmp handle table");
150 return -1;
151 }
152
153 slog("guest-file-open, handle: % " PRId64, fd);
154 return fd;
155}
156
157void qmp_guest_file_close(int64_t handle, Error **errp)
158{
159 bool ret;
160 GuestFileHandle *gfh = guest_file_handle_find(handle, errp);
161 slog("guest-file-close called, handle: %" PRId64, handle);
162 if (gfh == NULL) {
163 return;
164 }
165 ret = CloseHandle(gfh->fh);
166 if (!ret) {
167 error_setg_win32(errp, GetLastError(), "failed close handle");
168 return;
169 }
170
171 QTAILQ_REMOVE(&guest_file_state.filehandles, gfh, next);
172 g_free(gfh);
173}
174
77dbc81b 175static void acquire_privilege(const char *name, Error **errp)
d8ca685a 176{
374044f0 177 HANDLE token = NULL;
546b60d0 178 TOKEN_PRIVILEGES priv;
aa59637e
GH
179 Error *local_err = NULL;
180
aa59637e
GH
181 if (OpenProcessToken(GetCurrentProcess(),
182 TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, &token))
183 {
184 if (!LookupPrivilegeValue(NULL, name, &priv.Privileges[0].Luid)) {
c6bd8c70
MA
185 error_setg(&local_err, QERR_QGA_COMMAND_FAILED,
186 "no luid for requested privilege");
aa59637e
GH
187 goto out;
188 }
189
190 priv.PrivilegeCount = 1;
191 priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
192
193 if (!AdjustTokenPrivileges(token, FALSE, &priv, 0, NULL, 0)) {
c6bd8c70
MA
194 error_setg(&local_err, QERR_QGA_COMMAND_FAILED,
195 "unable to acquire requested privilege");
aa59637e
GH
196 goto out;
197 }
198
aa59637e 199 } else {
c6bd8c70
MA
200 error_setg(&local_err, QERR_QGA_COMMAND_FAILED,
201 "failed to open privilege token");
aa59637e
GH
202 }
203
204out:
374044f0
GA
205 if (token) {
206 CloseHandle(token);
207 }
aa59637e 208 if (local_err) {
77dbc81b 209 error_propagate(errp, local_err);
aa59637e
GH
210 }
211}
212
77dbc81b
MA
213static void execute_async(DWORD WINAPI (*func)(LPVOID), LPVOID opaque,
214 Error **errp)
aa59637e
GH
215{
216 Error *local_err = NULL;
217
aa59637e
GH
218 HANDLE thread = CreateThread(NULL, 0, func, opaque, 0, NULL);
219 if (!thread) {
c6bd8c70
MA
220 error_setg(&local_err, QERR_QGA_COMMAND_FAILED,
221 "failed to dispatch asynchronous command");
77dbc81b 222 error_propagate(errp, local_err);
aa59637e
GH
223 }
224}
225
77dbc81b 226void qmp_guest_shutdown(bool has_mode, const char *mode, Error **errp)
aa59637e 227{
0f230bf7 228 Error *local_err = NULL;
546b60d0
MR
229 UINT shutdown_flag = EWX_FORCE;
230
231 slog("guest-shutdown called, mode: %s", mode);
232
233 if (!has_mode || strcmp(mode, "powerdown") == 0) {
234 shutdown_flag |= EWX_POWEROFF;
235 } else if (strcmp(mode, "halt") == 0) {
236 shutdown_flag |= EWX_SHUTDOWN;
237 } else if (strcmp(mode, "reboot") == 0) {
238 shutdown_flag |= EWX_REBOOT;
239 } else {
c6bd8c70
MA
240 error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "mode",
241 "halt|powerdown|reboot");
546b60d0
MR
242 return;
243 }
244
245 /* Request a shutdown privilege, but try to shut down the system
246 anyway. */
0f230bf7
MA
247 acquire_privilege(SE_SHUTDOWN_NAME, &local_err);
248 if (local_err) {
249 error_propagate(errp, local_err);
aa59637e 250 return;
546b60d0
MR
251 }
252
253 if (!ExitWindowsEx(shutdown_flag, SHTDN_REASON_FLAG_PLANNED)) {
16f4e8fa 254 slog("guest-shutdown failed: %lu", GetLastError());
c6bd8c70 255 error_setg(errp, QERR_UNDEFINED_ERROR);
546b60d0 256 }
d8ca685a
MR
257}
258
d8ca685a 259GuestFileRead *qmp_guest_file_read(int64_t handle, bool has_count,
77dbc81b 260 int64_t count, Error **errp)
d8ca685a 261{
fa193594
OK
262 GuestFileRead *read_data = NULL;
263 guchar *buf;
264 HANDLE fh;
265 bool is_ok;
266 DWORD read_count;
267 GuestFileHandle *gfh = guest_file_handle_find(handle, errp);
268
269 if (!gfh) {
270 return NULL;
271 }
272 if (!has_count) {
273 count = QGA_READ_COUNT_DEFAULT;
274 } else if (count < 0) {
275 error_setg(errp, "value '%" PRId64
276 "' is invalid for argument count", count);
277 return NULL;
278 }
279
280 fh = gfh->fh;
281 buf = g_malloc0(count+1);
282 is_ok = ReadFile(fh, buf, count, &read_count, NULL);
283 if (!is_ok) {
284 error_setg_win32(errp, GetLastError(), "failed to read file");
285 slog("guest-file-read failed, handle %" PRId64, handle);
286 } else {
287 buf[read_count] = 0;
288 read_data = g_malloc0(sizeof(GuestFileRead));
289 read_data->count = (size_t)read_count;
290 read_data->eof = read_count == 0;
291
292 if (read_count != 0) {
293 read_data->buf_b64 = g_base64_encode(buf, read_count);
294 }
295 }
296 g_free(buf);
297
298 return read_data;
d8ca685a
MR
299}
300
301GuestFileWrite *qmp_guest_file_write(int64_t handle, const char *buf_b64,
77dbc81b
MA
302 bool has_count, int64_t count,
303 Error **errp)
d8ca685a 304{
fa193594
OK
305 GuestFileWrite *write_data = NULL;
306 guchar *buf;
307 gsize buf_len;
308 bool is_ok;
309 DWORD write_count;
310 GuestFileHandle *gfh = guest_file_handle_find(handle, errp);
311 HANDLE fh;
312
313 if (!gfh) {
314 return NULL;
315 }
316 fh = gfh->fh;
317 buf = g_base64_decode(buf_b64, &buf_len);
318
319 if (!has_count) {
320 count = buf_len;
321 } else if (count < 0 || count > buf_len) {
322 error_setg(errp, "value '%" PRId64
323 "' is invalid for argument count", count);
324 goto done;
325 }
326
327 is_ok = WriteFile(fh, buf, count, &write_count, NULL);
328 if (!is_ok) {
329 error_setg_win32(errp, GetLastError(), "failed to write to file");
330 slog("guest-file-write-failed, handle: %" PRId64, handle);
331 } else {
332 write_data = g_malloc0(sizeof(GuestFileWrite));
333 write_data->count = (size_t) write_count;
334 }
335
336done:
337 g_free(buf);
338 return write_data;
d8ca685a
MR
339}
340
341GuestFileSeek *qmp_guest_file_seek(int64_t handle, int64_t offset,
77dbc81b 342 int64_t whence, Error **errp)
d8ca685a 343{
fa193594
OK
344 GuestFileHandle *gfh;
345 GuestFileSeek *seek_data;
346 HANDLE fh;
347 LARGE_INTEGER new_pos, off_pos;
348 off_pos.QuadPart = offset;
349 BOOL res;
350 gfh = guest_file_handle_find(handle, errp);
351 if (!gfh) {
352 return NULL;
353 }
354
355 fh = gfh->fh;
356 res = SetFilePointerEx(fh, off_pos, &new_pos, whence);
357 if (!res) {
358 error_setg_win32(errp, GetLastError(), "failed to seek file");
359 return NULL;
360 }
361 seek_data = g_new0(GuestFileSeek, 1);
362 seek_data->position = new_pos.QuadPart;
363 return seek_data;
d8ca685a
MR
364}
365
77dbc81b 366void qmp_guest_file_flush(int64_t handle, Error **errp)
d8ca685a 367{
fa193594
OK
368 HANDLE fh;
369 GuestFileHandle *gfh = guest_file_handle_find(handle, errp);
370 if (!gfh) {
371 return;
372 }
373
374 fh = gfh->fh;
375 if (!FlushFileBuffers(fh)) {
376 error_setg_win32(errp, GetLastError(), "failed to flush file");
377 }
378}
379
380static void guest_file_init(void)
381{
382 QTAILQ_INIT(&guest_file_state.filehandles);
d8ca685a
MR
383}
384
46d4c572
TS
385GuestFilesystemInfoList *qmp_guest_get_fsinfo(Error **errp)
386{
c6bd8c70 387 error_setg(errp, QERR_UNSUPPORTED);
46d4c572
TS
388 return NULL;
389}
390
d8ca685a
MR
391/*
392 * Return status of freeze/thaw
393 */
77dbc81b 394GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **errp)
d8ca685a 395{
64c00317 396 if (!vss_initialized()) {
c6bd8c70 397 error_setg(errp, QERR_UNSUPPORTED);
64c00317
TS
398 return 0;
399 }
400
401 if (ga_is_frozen(ga_state)) {
402 return GUEST_FSFREEZE_STATUS_FROZEN;
403 }
404
405 return GUEST_FSFREEZE_STATUS_THAWED;
d8ca685a
MR
406}
407
408/*
64c00317
TS
409 * Freeze local file systems using Volume Shadow-copy Service.
410 * The frozen state is limited for up to 10 seconds by VSS.
d8ca685a 411 */
77dbc81b 412int64_t qmp_guest_fsfreeze_freeze(Error **errp)
d8ca685a 413{
64c00317
TS
414 int i;
415 Error *local_err = NULL;
416
417 if (!vss_initialized()) {
c6bd8c70 418 error_setg(errp, QERR_UNSUPPORTED);
64c00317
TS
419 return 0;
420 }
421
422 slog("guest-fsfreeze called");
423
424 /* cannot risk guest agent blocking itself on a write in this state */
425 ga_set_frozen(ga_state);
426
0f230bf7
MA
427 qga_vss_fsfreeze(&i, &local_err, true);
428 if (local_err) {
429 error_propagate(errp, local_err);
64c00317
TS
430 goto error;
431 }
432
433 return i;
434
435error:
0f230bf7 436 local_err = NULL;
64c00317 437 qmp_guest_fsfreeze_thaw(&local_err);
84d18f06 438 if (local_err) {
64c00317
TS
439 g_debug("cleanup thaw: %s", error_get_pretty(local_err));
440 error_free(local_err);
441 }
d8ca685a
MR
442 return 0;
443}
444
e99bce20
TS
445int64_t qmp_guest_fsfreeze_freeze_list(bool has_mountpoints,
446 strList *mountpoints,
447 Error **errp)
448{
c6bd8c70 449 error_setg(errp, QERR_UNSUPPORTED);
e99bce20
TS
450
451 return 0;
452}
453
d8ca685a 454/*
64c00317 455 * Thaw local file systems using Volume Shadow-copy Service.
d8ca685a 456 */
77dbc81b 457int64_t qmp_guest_fsfreeze_thaw(Error **errp)
d8ca685a 458{
64c00317
TS
459 int i;
460
461 if (!vss_initialized()) {
c6bd8c70 462 error_setg(errp, QERR_UNSUPPORTED);
64c00317
TS
463 return 0;
464 }
465
77dbc81b 466 qga_vss_fsfreeze(&i, errp, false);
64c00317
TS
467
468 ga_unset_frozen(ga_state);
469 return i;
470}
471
472static void guest_fsfreeze_cleanup(void)
473{
474 Error *err = NULL;
475
476 if (!vss_initialized()) {
477 return;
478 }
479
480 if (ga_is_frozen(ga_state) == GUEST_FSFREEZE_STATUS_FROZEN) {
481 qmp_guest_fsfreeze_thaw(&err);
482 if (err) {
483 slog("failed to clean up frozen filesystems: %s",
484 error_get_pretty(err));
485 error_free(err);
486 }
487 }
488
489 vss_deinit(true);
d8ca685a
MR
490}
491
eab5fd59
PB
492/*
493 * Walk list of mounted file systems in the guest, and discard unused
494 * areas.
495 */
e82855d9
JO
496GuestFilesystemTrimResponse *
497qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp)
eab5fd59 498{
c6bd8c70 499 error_setg(errp, QERR_UNSUPPORTED);
e82855d9 500 return NULL;
eab5fd59
PB
501}
502
aa59637e 503typedef enum {
f54603b6
MR
504 GUEST_SUSPEND_MODE_DISK,
505 GUEST_SUSPEND_MODE_RAM
aa59637e
GH
506} GuestSuspendMode;
507
77dbc81b 508static void check_suspend_mode(GuestSuspendMode mode, Error **errp)
aa59637e
GH
509{
510 SYSTEM_POWER_CAPABILITIES sys_pwr_caps;
511 Error *local_err = NULL;
512
aa59637e
GH
513 ZeroMemory(&sys_pwr_caps, sizeof(sys_pwr_caps));
514 if (!GetPwrCapabilities(&sys_pwr_caps)) {
c6bd8c70
MA
515 error_setg(&local_err, QERR_QGA_COMMAND_FAILED,
516 "failed to determine guest suspend capabilities");
aa59637e
GH
517 goto out;
518 }
519
f54603b6
MR
520 switch (mode) {
521 case GUEST_SUSPEND_MODE_DISK:
522 if (!sys_pwr_caps.SystemS4) {
c6bd8c70
MA
523 error_setg(&local_err, QERR_QGA_COMMAND_FAILED,
524 "suspend-to-disk not supported by OS");
aa59637e 525 }
f54603b6
MR
526 break;
527 case GUEST_SUSPEND_MODE_RAM:
528 if (!sys_pwr_caps.SystemS3) {
c6bd8c70
MA
529 error_setg(&local_err, QERR_QGA_COMMAND_FAILED,
530 "suspend-to-ram not supported by OS");
f54603b6
MR
531 }
532 break;
533 default:
c6bd8c70
MA
534 error_setg(&local_err, QERR_INVALID_PARAMETER_VALUE, "mode",
535 "GuestSuspendMode");
aa59637e
GH
536 }
537
aa59637e
GH
538out:
539 if (local_err) {
77dbc81b 540 error_propagate(errp, local_err);
aa59637e
GH
541 }
542}
543
544static DWORD WINAPI do_suspend(LPVOID opaque)
545{
546 GuestSuspendMode *mode = opaque;
547 DWORD ret = 0;
548
549 if (!SetSuspendState(*mode == GUEST_SUSPEND_MODE_DISK, TRUE, TRUE)) {
16f4e8fa 550 slog("failed to suspend guest, %lu", GetLastError());
aa59637e
GH
551 ret = -1;
552 }
553 g_free(mode);
554 return ret;
555}
556
77dbc81b 557void qmp_guest_suspend_disk(Error **errp)
11d0f125 558{
0f230bf7 559 Error *local_err = NULL;
aa59637e
GH
560 GuestSuspendMode *mode = g_malloc(sizeof(GuestSuspendMode));
561
562 *mode = GUEST_SUSPEND_MODE_DISK;
0f230bf7
MA
563 check_suspend_mode(*mode, &local_err);
564 acquire_privilege(SE_SHUTDOWN_NAME, &local_err);
565 execute_async(do_suspend, mode, &local_err);
aa59637e 566
0f230bf7
MA
567 if (local_err) {
568 error_propagate(errp, local_err);
aa59637e
GH
569 g_free(mode);
570 }
11d0f125
LC
571}
572
77dbc81b 573void qmp_guest_suspend_ram(Error **errp)
fbf42210 574{
0f230bf7 575 Error *local_err = NULL;
f54603b6
MR
576 GuestSuspendMode *mode = g_malloc(sizeof(GuestSuspendMode));
577
578 *mode = GUEST_SUSPEND_MODE_RAM;
0f230bf7
MA
579 check_suspend_mode(*mode, &local_err);
580 acquire_privilege(SE_SHUTDOWN_NAME, &local_err);
581 execute_async(do_suspend, mode, &local_err);
f54603b6 582
0f230bf7
MA
583 if (local_err) {
584 error_propagate(errp, local_err);
f54603b6
MR
585 g_free(mode);
586 }
fbf42210
LC
587}
588
77dbc81b 589void qmp_guest_suspend_hybrid(Error **errp)
95f4f404 590{
c6bd8c70 591 error_setg(errp, QERR_UNSUPPORTED);
95f4f404
LC
592}
593
77dbc81b 594GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp)
3424fc9f 595{
c6bd8c70 596 error_setg(errp, QERR_UNSUPPORTED);
3424fc9f
MP
597 return NULL;
598}
599
6912e6a9
LL
600int64_t qmp_guest_get_time(Error **errp)
601{
3f2a6087
LL
602 SYSTEMTIME ts = {0};
603 int64_t time_ns;
604 FILETIME tf;
605
606 GetSystemTime(&ts);
607 if (ts.wYear < 1601 || ts.wYear > 30827) {
608 error_setg(errp, "Failed to get time");
609 return -1;
610 }
611
612 if (!SystemTimeToFileTime(&ts, &tf)) {
613 error_setg(errp, "Failed to convert system time: %d", (int)GetLastError());
614 return -1;
615 }
616
617 time_ns = ((((int64_t)tf.dwHighDateTime << 32) | tf.dwLowDateTime)
618 - W32_FT_OFFSET) * 100;
619
620 return time_ns;
6912e6a9
LL
621}
622
2c958923 623void qmp_guest_set_time(bool has_time, int64_t time_ns, Error **errp)
a1bca57f 624{
0f230bf7 625 Error *local_err = NULL;
b8f954fe
LL
626 SYSTEMTIME ts;
627 FILETIME tf;
628 LONGLONG time;
629
ee17cbdc
MP
630 if (!has_time) {
631 /* Unfortunately, Windows libraries don't provide an easy way to access
632 * RTC yet:
633 *
634 * https://msdn.microsoft.com/en-us/library/aa908981.aspx
635 */
636 error_setg(errp, "Time argument is required on this platform");
637 return;
638 }
639
640 /* Validate time passed by user. */
641 if (time_ns < 0 || time_ns / 100 > INT64_MAX - W32_FT_OFFSET) {
642 error_setg(errp, "Time %" PRId64 "is invalid", time_ns);
643 return;
644 }
b8f954fe 645
ee17cbdc 646 time = time_ns / 100 + W32_FT_OFFSET;
b8f954fe 647
ee17cbdc
MP
648 tf.dwLowDateTime = (DWORD) time;
649 tf.dwHighDateTime = (DWORD) (time >> 32);
b8f954fe 650
ee17cbdc
MP
651 if (!FileTimeToSystemTime(&tf, &ts)) {
652 error_setg(errp, "Failed to convert system time %d",
653 (int)GetLastError());
654 return;
b8f954fe
LL
655 }
656
0f230bf7
MA
657 acquire_privilege(SE_SYSTEMTIME_NAME, &local_err);
658 if (local_err) {
659 error_propagate(errp, local_err);
b8f954fe
LL
660 return;
661 }
662
663 if (!SetSystemTime(&ts)) {
664 error_setg(errp, "Failed to set time to guest: %d", (int)GetLastError());
665 return;
666 }
a1bca57f
LL
667}
668
70e133a7
LE
669GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp)
670{
c6bd8c70 671 error_setg(errp, QERR_UNSUPPORTED);
70e133a7
LE
672 return NULL;
673}
674
675int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp)
676{
c6bd8c70 677 error_setg(errp, QERR_UNSUPPORTED);
70e133a7
LE
678 return -1;
679}
680
215a2771
DB
681void qmp_guest_set_user_password(const char *username,
682 const char *password,
683 bool crypted,
684 Error **errp)
685{
c6bd8c70 686 error_setg(errp, QERR_UNSUPPORTED);
215a2771
DB
687}
688
a065aaa9
HZ
689GuestMemoryBlockList *qmp_guest_get_memory_blocks(Error **errp)
690{
c6bd8c70 691 error_setg(errp, QERR_UNSUPPORTED);
a065aaa9
HZ
692 return NULL;
693}
694
695GuestMemoryBlockResponseList *
696qmp_guest_set_memory_blocks(GuestMemoryBlockList *mem_blks, Error **errp)
697{
c6bd8c70 698 error_setg(errp, QERR_UNSUPPORTED);
a065aaa9
HZ
699 return NULL;
700}
701
702GuestMemoryBlockInfo *qmp_guest_get_memory_block_info(Error **errp)
703{
c6bd8c70 704 error_setg(errp, QERR_UNSUPPORTED);
a065aaa9
HZ
705 return NULL;
706}
707
1281c08a
TS
708/* add unsupported commands to the blacklist */
709GList *ga_command_blacklist_init(GList *blacklist)
710{
711 const char *list_unsupported[] = {
1281c08a
TS
712 "guest-suspend-hybrid", "guest-network-get-interfaces",
713 "guest-get-vcpus", "guest-set-vcpus",
215a2771 714 "guest-set-user-password",
0dd38a03
HZ
715 "guest-get-memory-blocks", "guest-set-memory-blocks",
716 "guest-get-memory-block-size",
1281c08a
TS
717 "guest-fsfreeze-freeze-list", "guest-get-fsinfo",
718 "guest-fstrim", NULL};
719 char **p = (char **)list_unsupported;
720
721 while (*p) {
722 blacklist = g_list_append(blacklist, *p++);
723 }
724
725 if (!vss_init(true)) {
c69403fc 726 g_debug("vss_init failed, vss commands are going to be disabled");
1281c08a
TS
727 const char *list[] = {
728 "guest-get-fsinfo", "guest-fsfreeze-status",
729 "guest-fsfreeze-freeze", "guest-fsfreeze-thaw", NULL};
730 p = (char **)list;
731
732 while (*p) {
733 blacklist = g_list_append(blacklist, *p++);
734 }
735 }
736
737 return blacklist;
738}
739
d8ca685a
MR
740/* register init/cleanup routines for stateful command groups */
741void ga_command_state_init(GAState *s, GACommandState *cs)
742{
1281c08a 743 if (!vss_initialized()) {
64c00317
TS
744 ga_command_state_add(cs, NULL, guest_fsfreeze_cleanup);
745 }
fa193594 746 ga_command_state_add(cs, guest_file_init, NULL);
d8ca685a 747}