]> git.proxmox.com Git - mirror_qemu.git/blame - qga/commands-win32.c
qga: added empty qmp_quest_get_fsinfo functionality.
[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>
d6c5528b
KA
19#include <winsock2.h>
20#include <ws2tcpip.h>
21#include <iptypes.h>
22#include <iphlpapi.h>
d8ca685a 23#include "qga/guest-agent-core.h"
64c00317 24#include "qga/vss-win32.h"
d8ca685a 25#include "qga-qmp-commands.h"
7b1b5d19 26#include "qapi/qmp/qerror.h"
fa193594 27#include "qemu/queue.h"
d6c5528b 28#include "qemu/host-utils.h"
d8ca685a 29
546b60d0
MR
30#ifndef SHTDN_REASON_FLAG_PLANNED
31#define SHTDN_REASON_FLAG_PLANNED 0x80000000
32#endif
33
3f2a6087
LL
34/* multiple of 100 nanoseconds elapsed between windows baseline
35 * (1/1/1601) and Unix Epoch (1/1/1970), accounting for leap years */
36#define W32_FT_OFFSET (10000000ULL * 60 * 60 * 24 * \
37 (365 * (1970 - 1601) + \
38 (1970 - 1601) / 4 - 3))
39
fa193594
OK
40#define INVALID_SET_FILE_POINTER ((DWORD)-1)
41
42typedef struct GuestFileHandle {
43 int64_t id;
44 HANDLE fh;
45 QTAILQ_ENTRY(GuestFileHandle) next;
46} GuestFileHandle;
47
48static struct {
49 QTAILQ_HEAD(, GuestFileHandle) filehandles;
50} guest_file_state;
51
52
53typedef struct OpenFlags {
54 const char *forms;
55 DWORD desired_access;
56 DWORD creation_disposition;
57} OpenFlags;
58static OpenFlags guest_file_open_modes[] = {
59 {"r", GENERIC_READ, OPEN_EXISTING},
60 {"rb", GENERIC_READ, OPEN_EXISTING},
61 {"w", GENERIC_WRITE, CREATE_ALWAYS},
62 {"wb", GENERIC_WRITE, CREATE_ALWAYS},
63 {"a", GENERIC_WRITE, OPEN_ALWAYS },
64 {"r+", GENERIC_WRITE|GENERIC_READ, OPEN_EXISTING},
65 {"rb+", GENERIC_WRITE|GENERIC_READ, OPEN_EXISTING},
66 {"r+b", GENERIC_WRITE|GENERIC_READ, OPEN_EXISTING},
67 {"w+", GENERIC_WRITE|GENERIC_READ, CREATE_ALWAYS},
68 {"wb+", GENERIC_WRITE|GENERIC_READ, CREATE_ALWAYS},
69 {"w+b", GENERIC_WRITE|GENERIC_READ, CREATE_ALWAYS},
70 {"a+", GENERIC_WRITE|GENERIC_READ, OPEN_ALWAYS },
71 {"ab+", GENERIC_WRITE|GENERIC_READ, OPEN_ALWAYS },
72 {"a+b", GENERIC_WRITE|GENERIC_READ, OPEN_ALWAYS }
73};
74
75static OpenFlags *find_open_flag(const char *mode_str)
76{
77 int mode;
78 Error **errp = NULL;
79
80 for (mode = 0; mode < ARRAY_SIZE(guest_file_open_modes); ++mode) {
81 OpenFlags *flags = guest_file_open_modes + mode;
82
83 if (strcmp(flags->forms, mode_str) == 0) {
84 return flags;
85 }
86 }
87
88 error_setg(errp, "invalid file open mode '%s'", mode_str);
89 return NULL;
90}
91
92static int64_t guest_file_handle_add(HANDLE fh, Error **errp)
93{
94 GuestFileHandle *gfh;
95 int64_t handle;
96
97 handle = ga_get_fd_handle(ga_state, errp);
98 if (handle < 0) {
99 return -1;
100 }
101 gfh = g_malloc0(sizeof(GuestFileHandle));
102 gfh->id = handle;
103 gfh->fh = fh;
104 QTAILQ_INSERT_TAIL(&guest_file_state.filehandles, gfh, next);
105
106 return handle;
107}
108
109static GuestFileHandle *guest_file_handle_find(int64_t id, Error **errp)
110{
111 GuestFileHandle *gfh;
112 QTAILQ_FOREACH(gfh, &guest_file_state.filehandles, next) {
113 if (gfh->id == id) {
114 return gfh;
115 }
116 }
117 error_setg(errp, "handle '%" PRId64 "' has not been found", id);
118 return NULL;
119}
120
121int64_t qmp_guest_file_open(const char *path, bool has_mode,
122 const char *mode, Error **errp)
123{
124 int64_t fd;
125 HANDLE fh;
126 HANDLE templ_file = NULL;
127 DWORD share_mode = FILE_SHARE_READ;
128 DWORD flags_and_attr = FILE_ATTRIBUTE_NORMAL;
129 LPSECURITY_ATTRIBUTES sa_attr = NULL;
130 OpenFlags *guest_flags;
131
132 if (!has_mode) {
133 mode = "r";
134 }
135 slog("guest-file-open called, filepath: %s, mode: %s", path, mode);
136 guest_flags = find_open_flag(mode);
137 if (guest_flags == NULL) {
138 error_setg(errp, "invalid file open mode");
139 return -1;
140 }
141
142 fh = CreateFile(path, guest_flags->desired_access, share_mode, sa_attr,
143 guest_flags->creation_disposition, flags_and_attr,
144 templ_file);
145 if (fh == INVALID_HANDLE_VALUE) {
146 error_setg_win32(errp, GetLastError(), "failed to open file '%s'",
147 path);
148 return -1;
149 }
150
151 fd = guest_file_handle_add(fh, errp);
152 if (fd < 0) {
153 CloseHandle(&fh);
154 error_setg(errp, "failed to add handle to qmp handle table");
155 return -1;
156 }
157
158 slog("guest-file-open, handle: % " PRId64, fd);
159 return fd;
160}
161
162void qmp_guest_file_close(int64_t handle, Error **errp)
163{
164 bool ret;
165 GuestFileHandle *gfh = guest_file_handle_find(handle, errp);
166 slog("guest-file-close called, handle: %" PRId64, handle);
167 if (gfh == NULL) {
168 return;
169 }
170 ret = CloseHandle(gfh->fh);
171 if (!ret) {
172 error_setg_win32(errp, GetLastError(), "failed close handle");
173 return;
174 }
175
176 QTAILQ_REMOVE(&guest_file_state.filehandles, gfh, next);
177 g_free(gfh);
178}
179
77dbc81b 180static void acquire_privilege(const char *name, Error **errp)
d8ca685a 181{
374044f0 182 HANDLE token = NULL;
546b60d0 183 TOKEN_PRIVILEGES priv;
aa59637e
GH
184 Error *local_err = NULL;
185
aa59637e
GH
186 if (OpenProcessToken(GetCurrentProcess(),
187 TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, &token))
188 {
189 if (!LookupPrivilegeValue(NULL, name, &priv.Privileges[0].Luid)) {
c6bd8c70
MA
190 error_setg(&local_err, QERR_QGA_COMMAND_FAILED,
191 "no luid for requested privilege");
aa59637e
GH
192 goto out;
193 }
194
195 priv.PrivilegeCount = 1;
196 priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
197
198 if (!AdjustTokenPrivileges(token, FALSE, &priv, 0, NULL, 0)) {
c6bd8c70
MA
199 error_setg(&local_err, QERR_QGA_COMMAND_FAILED,
200 "unable to acquire requested privilege");
aa59637e
GH
201 goto out;
202 }
203
aa59637e 204 } else {
c6bd8c70
MA
205 error_setg(&local_err, QERR_QGA_COMMAND_FAILED,
206 "failed to open privilege token");
aa59637e
GH
207 }
208
209out:
374044f0
GA
210 if (token) {
211 CloseHandle(token);
212 }
aa59637e 213 if (local_err) {
77dbc81b 214 error_propagate(errp, local_err);
aa59637e
GH
215 }
216}
217
77dbc81b
MA
218static void execute_async(DWORD WINAPI (*func)(LPVOID), LPVOID opaque,
219 Error **errp)
aa59637e
GH
220{
221 Error *local_err = NULL;
222
aa59637e
GH
223 HANDLE thread = CreateThread(NULL, 0, func, opaque, 0, NULL);
224 if (!thread) {
c6bd8c70
MA
225 error_setg(&local_err, QERR_QGA_COMMAND_FAILED,
226 "failed to dispatch asynchronous command");
77dbc81b 227 error_propagate(errp, local_err);
aa59637e
GH
228 }
229}
230
77dbc81b 231void qmp_guest_shutdown(bool has_mode, const char *mode, Error **errp)
aa59637e 232{
0f230bf7 233 Error *local_err = NULL;
546b60d0
MR
234 UINT shutdown_flag = EWX_FORCE;
235
236 slog("guest-shutdown called, mode: %s", mode);
237
238 if (!has_mode || strcmp(mode, "powerdown") == 0) {
239 shutdown_flag |= EWX_POWEROFF;
240 } else if (strcmp(mode, "halt") == 0) {
241 shutdown_flag |= EWX_SHUTDOWN;
242 } else if (strcmp(mode, "reboot") == 0) {
243 shutdown_flag |= EWX_REBOOT;
244 } else {
c6bd8c70
MA
245 error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "mode",
246 "halt|powerdown|reboot");
546b60d0
MR
247 return;
248 }
249
250 /* Request a shutdown privilege, but try to shut down the system
251 anyway. */
0f230bf7
MA
252 acquire_privilege(SE_SHUTDOWN_NAME, &local_err);
253 if (local_err) {
254 error_propagate(errp, local_err);
aa59637e 255 return;
546b60d0
MR
256 }
257
258 if (!ExitWindowsEx(shutdown_flag, SHTDN_REASON_FLAG_PLANNED)) {
16f4e8fa 259 slog("guest-shutdown failed: %lu", GetLastError());
c6bd8c70 260 error_setg(errp, QERR_UNDEFINED_ERROR);
546b60d0 261 }
d8ca685a
MR
262}
263
d8ca685a 264GuestFileRead *qmp_guest_file_read(int64_t handle, bool has_count,
77dbc81b 265 int64_t count, Error **errp)
d8ca685a 266{
fa193594
OK
267 GuestFileRead *read_data = NULL;
268 guchar *buf;
269 HANDLE fh;
270 bool is_ok;
271 DWORD read_count;
272 GuestFileHandle *gfh = guest_file_handle_find(handle, errp);
273
274 if (!gfh) {
275 return NULL;
276 }
277 if (!has_count) {
278 count = QGA_READ_COUNT_DEFAULT;
279 } else if (count < 0) {
280 error_setg(errp, "value '%" PRId64
281 "' is invalid for argument count", count);
282 return NULL;
283 }
284
285 fh = gfh->fh;
286 buf = g_malloc0(count+1);
287 is_ok = ReadFile(fh, buf, count, &read_count, NULL);
288 if (!is_ok) {
289 error_setg_win32(errp, GetLastError(), "failed to read file");
290 slog("guest-file-read failed, handle %" PRId64, handle);
291 } else {
292 buf[read_count] = 0;
293 read_data = g_malloc0(sizeof(GuestFileRead));
294 read_data->count = (size_t)read_count;
295 read_data->eof = read_count == 0;
296
297 if (read_count != 0) {
298 read_data->buf_b64 = g_base64_encode(buf, read_count);
299 }
300 }
301 g_free(buf);
302
303 return read_data;
d8ca685a
MR
304}
305
306GuestFileWrite *qmp_guest_file_write(int64_t handle, const char *buf_b64,
77dbc81b
MA
307 bool has_count, int64_t count,
308 Error **errp)
d8ca685a 309{
fa193594
OK
310 GuestFileWrite *write_data = NULL;
311 guchar *buf;
312 gsize buf_len;
313 bool is_ok;
314 DWORD write_count;
315 GuestFileHandle *gfh = guest_file_handle_find(handle, errp);
316 HANDLE fh;
317
318 if (!gfh) {
319 return NULL;
320 }
321 fh = gfh->fh;
322 buf = g_base64_decode(buf_b64, &buf_len);
323
324 if (!has_count) {
325 count = buf_len;
326 } else if (count < 0 || count > buf_len) {
327 error_setg(errp, "value '%" PRId64
328 "' is invalid for argument count", count);
329 goto done;
330 }
331
332 is_ok = WriteFile(fh, buf, count, &write_count, NULL);
333 if (!is_ok) {
334 error_setg_win32(errp, GetLastError(), "failed to write to file");
335 slog("guest-file-write-failed, handle: %" PRId64, handle);
336 } else {
337 write_data = g_malloc0(sizeof(GuestFileWrite));
338 write_data->count = (size_t) write_count;
339 }
340
341done:
342 g_free(buf);
343 return write_data;
d8ca685a
MR
344}
345
346GuestFileSeek *qmp_guest_file_seek(int64_t handle, int64_t offset,
77dbc81b 347 int64_t whence, Error **errp)
d8ca685a 348{
fa193594
OK
349 GuestFileHandle *gfh;
350 GuestFileSeek *seek_data;
351 HANDLE fh;
352 LARGE_INTEGER new_pos, off_pos;
353 off_pos.QuadPart = offset;
354 BOOL res;
355 gfh = guest_file_handle_find(handle, errp);
356 if (!gfh) {
357 return NULL;
358 }
359
360 fh = gfh->fh;
361 res = SetFilePointerEx(fh, off_pos, &new_pos, whence);
362 if (!res) {
363 error_setg_win32(errp, GetLastError(), "failed to seek file");
364 return NULL;
365 }
366 seek_data = g_new0(GuestFileSeek, 1);
367 seek_data->position = new_pos.QuadPart;
368 return seek_data;
d8ca685a
MR
369}
370
77dbc81b 371void qmp_guest_file_flush(int64_t handle, Error **errp)
d8ca685a 372{
fa193594
OK
373 HANDLE fh;
374 GuestFileHandle *gfh = guest_file_handle_find(handle, errp);
375 if (!gfh) {
376 return;
377 }
378
379 fh = gfh->fh;
380 if (!FlushFileBuffers(fh)) {
381 error_setg_win32(errp, GetLastError(), "failed to flush file");
382 }
383}
384
385static void guest_file_init(void)
386{
387 QTAILQ_INIT(&guest_file_state.filehandles);
d8ca685a
MR
388}
389
46d4c572
TS
390GuestFilesystemInfoList *qmp_guest_get_fsinfo(Error **errp)
391{
ef0a03f2
OK
392 HANDLE vol_h;
393 GuestFilesystemInfoList *new, *ret = NULL;
394 char guid[256];
395
396 vol_h = FindFirstVolume(guid, sizeof(guid));
397 if (vol_h == INVALID_HANDLE_VALUE) {
398 error_setg_win32(errp, GetLastError(), "failed to find any volume");
399 return NULL;
400 }
401
402 do {
403 new = g_malloc(sizeof(*ret));
404 new->value = build_guest_fsinfo(guid, errp);
405 new->next = ret;
406 ret = new;
407 } while (FindNextVolume(vol_h, guid, sizeof(guid)));
408
409 if (GetLastError() != ERROR_NO_MORE_FILES) {
410 error_setg_win32(errp, GetLastError(), "failed to find next volume");
411 }
412
413 FindVolumeClose(vol_h);
414 return ret;
46d4c572
TS
415}
416
d8ca685a
MR
417/*
418 * Return status of freeze/thaw
419 */
77dbc81b 420GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **errp)
d8ca685a 421{
64c00317 422 if (!vss_initialized()) {
c6bd8c70 423 error_setg(errp, QERR_UNSUPPORTED);
64c00317
TS
424 return 0;
425 }
426
427 if (ga_is_frozen(ga_state)) {
428 return GUEST_FSFREEZE_STATUS_FROZEN;
429 }
430
431 return GUEST_FSFREEZE_STATUS_THAWED;
d8ca685a
MR
432}
433
434/*
64c00317
TS
435 * Freeze local file systems using Volume Shadow-copy Service.
436 * The frozen state is limited for up to 10 seconds by VSS.
d8ca685a 437 */
77dbc81b 438int64_t qmp_guest_fsfreeze_freeze(Error **errp)
d8ca685a 439{
64c00317
TS
440 int i;
441 Error *local_err = NULL;
442
443 if (!vss_initialized()) {
c6bd8c70 444 error_setg(errp, QERR_UNSUPPORTED);
64c00317
TS
445 return 0;
446 }
447
448 slog("guest-fsfreeze called");
449
450 /* cannot risk guest agent blocking itself on a write in this state */
451 ga_set_frozen(ga_state);
452
0f230bf7
MA
453 qga_vss_fsfreeze(&i, &local_err, true);
454 if (local_err) {
455 error_propagate(errp, local_err);
64c00317
TS
456 goto error;
457 }
458
459 return i;
460
461error:
0f230bf7 462 local_err = NULL;
64c00317 463 qmp_guest_fsfreeze_thaw(&local_err);
84d18f06 464 if (local_err) {
64c00317
TS
465 g_debug("cleanup thaw: %s", error_get_pretty(local_err));
466 error_free(local_err);
467 }
d8ca685a
MR
468 return 0;
469}
470
e99bce20
TS
471int64_t qmp_guest_fsfreeze_freeze_list(bool has_mountpoints,
472 strList *mountpoints,
473 Error **errp)
474{
c6bd8c70 475 error_setg(errp, QERR_UNSUPPORTED);
e99bce20
TS
476
477 return 0;
478}
479
d8ca685a 480/*
64c00317 481 * Thaw local file systems using Volume Shadow-copy Service.
d8ca685a 482 */
77dbc81b 483int64_t qmp_guest_fsfreeze_thaw(Error **errp)
d8ca685a 484{
64c00317
TS
485 int i;
486
487 if (!vss_initialized()) {
c6bd8c70 488 error_setg(errp, QERR_UNSUPPORTED);
64c00317
TS
489 return 0;
490 }
491
77dbc81b 492 qga_vss_fsfreeze(&i, errp, false);
64c00317
TS
493
494 ga_unset_frozen(ga_state);
495 return i;
496}
497
498static void guest_fsfreeze_cleanup(void)
499{
500 Error *err = NULL;
501
502 if (!vss_initialized()) {
503 return;
504 }
505
506 if (ga_is_frozen(ga_state) == GUEST_FSFREEZE_STATUS_FROZEN) {
507 qmp_guest_fsfreeze_thaw(&err);
508 if (err) {
509 slog("failed to clean up frozen filesystems: %s",
510 error_get_pretty(err));
511 error_free(err);
512 }
513 }
514
515 vss_deinit(true);
d8ca685a
MR
516}
517
eab5fd59
PB
518/*
519 * Walk list of mounted file systems in the guest, and discard unused
520 * areas.
521 */
e82855d9
JO
522GuestFilesystemTrimResponse *
523qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp)
eab5fd59 524{
c6bd8c70 525 error_setg(errp, QERR_UNSUPPORTED);
e82855d9 526 return NULL;
eab5fd59
PB
527}
528
aa59637e 529typedef enum {
f54603b6
MR
530 GUEST_SUSPEND_MODE_DISK,
531 GUEST_SUSPEND_MODE_RAM
aa59637e
GH
532} GuestSuspendMode;
533
77dbc81b 534static void check_suspend_mode(GuestSuspendMode mode, Error **errp)
aa59637e
GH
535{
536 SYSTEM_POWER_CAPABILITIES sys_pwr_caps;
537 Error *local_err = NULL;
538
aa59637e
GH
539 ZeroMemory(&sys_pwr_caps, sizeof(sys_pwr_caps));
540 if (!GetPwrCapabilities(&sys_pwr_caps)) {
c6bd8c70
MA
541 error_setg(&local_err, QERR_QGA_COMMAND_FAILED,
542 "failed to determine guest suspend capabilities");
aa59637e
GH
543 goto out;
544 }
545
f54603b6
MR
546 switch (mode) {
547 case GUEST_SUSPEND_MODE_DISK:
548 if (!sys_pwr_caps.SystemS4) {
c6bd8c70
MA
549 error_setg(&local_err, QERR_QGA_COMMAND_FAILED,
550 "suspend-to-disk not supported by OS");
aa59637e 551 }
f54603b6
MR
552 break;
553 case GUEST_SUSPEND_MODE_RAM:
554 if (!sys_pwr_caps.SystemS3) {
c6bd8c70
MA
555 error_setg(&local_err, QERR_QGA_COMMAND_FAILED,
556 "suspend-to-ram not supported by OS");
f54603b6
MR
557 }
558 break;
559 default:
c6bd8c70
MA
560 error_setg(&local_err, QERR_INVALID_PARAMETER_VALUE, "mode",
561 "GuestSuspendMode");
aa59637e
GH
562 }
563
aa59637e
GH
564out:
565 if (local_err) {
77dbc81b 566 error_propagate(errp, local_err);
aa59637e
GH
567 }
568}
569
570static DWORD WINAPI do_suspend(LPVOID opaque)
571{
572 GuestSuspendMode *mode = opaque;
573 DWORD ret = 0;
574
575 if (!SetSuspendState(*mode == GUEST_SUSPEND_MODE_DISK, TRUE, TRUE)) {
16f4e8fa 576 slog("failed to suspend guest, %lu", GetLastError());
aa59637e
GH
577 ret = -1;
578 }
579 g_free(mode);
580 return ret;
581}
582
77dbc81b 583void qmp_guest_suspend_disk(Error **errp)
11d0f125 584{
0f230bf7 585 Error *local_err = NULL;
aa59637e
GH
586 GuestSuspendMode *mode = g_malloc(sizeof(GuestSuspendMode));
587
588 *mode = GUEST_SUSPEND_MODE_DISK;
0f230bf7
MA
589 check_suspend_mode(*mode, &local_err);
590 acquire_privilege(SE_SHUTDOWN_NAME, &local_err);
591 execute_async(do_suspend, mode, &local_err);
aa59637e 592
0f230bf7
MA
593 if (local_err) {
594 error_propagate(errp, local_err);
aa59637e
GH
595 g_free(mode);
596 }
11d0f125
LC
597}
598
77dbc81b 599void qmp_guest_suspend_ram(Error **errp)
fbf42210 600{
0f230bf7 601 Error *local_err = NULL;
f54603b6
MR
602 GuestSuspendMode *mode = g_malloc(sizeof(GuestSuspendMode));
603
604 *mode = GUEST_SUSPEND_MODE_RAM;
0f230bf7
MA
605 check_suspend_mode(*mode, &local_err);
606 acquire_privilege(SE_SHUTDOWN_NAME, &local_err);
607 execute_async(do_suspend, mode, &local_err);
f54603b6 608
0f230bf7
MA
609 if (local_err) {
610 error_propagate(errp, local_err);
f54603b6
MR
611 g_free(mode);
612 }
fbf42210
LC
613}
614
77dbc81b 615void qmp_guest_suspend_hybrid(Error **errp)
95f4f404 616{
c6bd8c70 617 error_setg(errp, QERR_UNSUPPORTED);
95f4f404
LC
618}
619
d6c5528b 620static IP_ADAPTER_ADDRESSES *guest_get_adapters_addresses(Error **errp)
3424fc9f 621{
d6c5528b
KA
622 IP_ADAPTER_ADDRESSES *adptr_addrs = NULL;
623 ULONG adptr_addrs_len = 0;
624 DWORD ret;
625
626 /* Call the first time to get the adptr_addrs_len. */
627 GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX,
628 NULL, adptr_addrs, &adptr_addrs_len);
629
630 adptr_addrs = g_malloc(adptr_addrs_len);
631 ret = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX,
632 NULL, adptr_addrs, &adptr_addrs_len);
633 if (ret != ERROR_SUCCESS) {
634 error_setg_win32(errp, ret, "failed to get adapters addresses");
635 g_free(adptr_addrs);
636 adptr_addrs = NULL;
637 }
638 return adptr_addrs;
639}
640
641static char *guest_wctomb_dup(WCHAR *wstr)
642{
643 char *str;
644 size_t i;
645
646 i = wcslen(wstr) + 1;
647 str = g_malloc(i);
648 WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK,
649 wstr, -1, str, i, NULL, NULL);
650 return str;
651}
652
653static char *guest_addr_to_str(IP_ADAPTER_UNICAST_ADDRESS *ip_addr,
654 Error **errp)
655{
656 char addr_str[INET6_ADDRSTRLEN + INET_ADDRSTRLEN];
657 DWORD len;
658 int ret;
659
660 if (ip_addr->Address.lpSockaddr->sa_family == AF_INET ||
661 ip_addr->Address.lpSockaddr->sa_family == AF_INET6) {
662 len = sizeof(addr_str);
663 ret = WSAAddressToString(ip_addr->Address.lpSockaddr,
664 ip_addr->Address.iSockaddrLength,
665 NULL,
666 addr_str,
667 &len);
668 if (ret != 0) {
669 error_setg_win32(errp, WSAGetLastError(),
670 "failed address presentation form conversion");
671 return NULL;
672 }
673 return g_strdup(addr_str);
674 }
3424fc9f
MP
675 return NULL;
676}
677
d6c5528b
KA
678#if (_WIN32_WINNT >= 0x0600)
679static int64_t guest_ip_prefix(IP_ADAPTER_UNICAST_ADDRESS *ip_addr)
680{
681 /* For Windows Vista/2008 and newer, use the OnLinkPrefixLength
682 * field to obtain the prefix.
683 */
684 return ip_addr->OnLinkPrefixLength;
685}
686#else
687/* When using the Windows XP and 2003 build environment, do the best we can to
688 * figure out the prefix.
689 */
690static IP_ADAPTER_INFO *guest_get_adapters_info(void)
691{
692 IP_ADAPTER_INFO *adptr_info = NULL;
693 ULONG adptr_info_len = 0;
694 DWORD ret;
695
696 /* Call the first time to get the adptr_info_len. */
697 GetAdaptersInfo(adptr_info, &adptr_info_len);
698
699 adptr_info = g_malloc(adptr_info_len);
700 ret = GetAdaptersInfo(adptr_info, &adptr_info_len);
701 if (ret != ERROR_SUCCESS) {
702 g_free(adptr_info);
703 adptr_info = NULL;
704 }
705 return adptr_info;
706}
707
708static int64_t guest_ip_prefix(IP_ADAPTER_UNICAST_ADDRESS *ip_addr)
709{
710 int64_t prefix = -1; /* Use for AF_INET6 and unknown/undetermined values. */
711 IP_ADAPTER_INFO *adptr_info, *info;
712 IP_ADDR_STRING *ip;
713 struct in_addr *p;
714
715 if (ip_addr->Address.lpSockaddr->sa_family != AF_INET) {
716 return prefix;
717 }
718 adptr_info = guest_get_adapters_info();
719 if (adptr_info == NULL) {
720 return prefix;
721 }
722
723 /* Match up the passed in ip_addr with one found in adaptr_info.
724 * The matching one in adptr_info will have the netmask.
725 */
726 p = &((struct sockaddr_in *)ip_addr->Address.lpSockaddr)->sin_addr;
727 for (info = adptr_info; info; info = info->Next) {
728 for (ip = &info->IpAddressList; ip; ip = ip->Next) {
729 if (p->S_un.S_addr == inet_addr(ip->IpAddress.String)) {
730 prefix = ctpop32(inet_addr(ip->IpMask.String));
731 goto out;
732 }
733 }
734 }
735out:
736 g_free(adptr_info);
737 return prefix;
738}
739#endif
740
741GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp)
742{
743 IP_ADAPTER_ADDRESSES *adptr_addrs, *addr;
744 IP_ADAPTER_UNICAST_ADDRESS *ip_addr = NULL;
745 GuestNetworkInterfaceList *head = NULL, *cur_item = NULL;
746 GuestIpAddressList *head_addr, *cur_addr;
747 GuestNetworkInterfaceList *info;
748 GuestIpAddressList *address_item = NULL;
749 unsigned char *mac_addr;
750 char *addr_str;
751 WORD wsa_version;
752 WSADATA wsa_data;
753 int ret;
754
755 adptr_addrs = guest_get_adapters_addresses(errp);
756 if (adptr_addrs == NULL) {
757 return NULL;
758 }
759
760 /* Make WSA APIs available. */
761 wsa_version = MAKEWORD(2, 2);
762 ret = WSAStartup(wsa_version, &wsa_data);
763 if (ret != 0) {
764 error_setg_win32(errp, ret, "failed socket startup");
765 goto out;
766 }
767
768 for (addr = adptr_addrs; addr; addr = addr->Next) {
769 info = g_malloc0(sizeof(*info));
770
771 if (cur_item == NULL) {
772 head = cur_item = info;
773 } else {
774 cur_item->next = info;
775 cur_item = info;
776 }
777
778 info->value = g_malloc0(sizeof(*info->value));
779 info->value->name = guest_wctomb_dup(addr->FriendlyName);
780
781 if (addr->PhysicalAddressLength != 0) {
782 mac_addr = addr->PhysicalAddress;
783
784 info->value->hardware_address =
785 g_strdup_printf("%02x:%02x:%02x:%02x:%02x:%02x",
786 (int) mac_addr[0], (int) mac_addr[1],
787 (int) mac_addr[2], (int) mac_addr[3],
788 (int) mac_addr[4], (int) mac_addr[5]);
789
790 info->value->has_hardware_address = true;
791 }
792
793 head_addr = NULL;
794 cur_addr = NULL;
795 for (ip_addr = addr->FirstUnicastAddress;
796 ip_addr;
797 ip_addr = ip_addr->Next) {
798 addr_str = guest_addr_to_str(ip_addr, errp);
799 if (addr_str == NULL) {
800 continue;
801 }
802
803 address_item = g_malloc0(sizeof(*address_item));
804
805 if (!cur_addr) {
806 head_addr = cur_addr = address_item;
807 } else {
808 cur_addr->next = address_item;
809 cur_addr = address_item;
810 }
811
812 address_item->value = g_malloc0(sizeof(*address_item->value));
813 address_item->value->ip_address = addr_str;
814 address_item->value->prefix = guest_ip_prefix(ip_addr);
815 if (ip_addr->Address.lpSockaddr->sa_family == AF_INET) {
816 address_item->value->ip_address_type =
817 GUEST_IP_ADDRESS_TYPE_IPV4;
818 } else if (ip_addr->Address.lpSockaddr->sa_family == AF_INET6) {
819 address_item->value->ip_address_type =
820 GUEST_IP_ADDRESS_TYPE_IPV6;
821 }
822 }
823 if (head_addr) {
824 info->value->has_ip_addresses = true;
825 info->value->ip_addresses = head_addr;
826 }
827 }
828 WSACleanup();
829out:
830 g_free(adptr_addrs);
831 return head;
832}
833
6912e6a9
LL
834int64_t qmp_guest_get_time(Error **errp)
835{
3f2a6087
LL
836 SYSTEMTIME ts = {0};
837 int64_t time_ns;
838 FILETIME tf;
839
840 GetSystemTime(&ts);
841 if (ts.wYear < 1601 || ts.wYear > 30827) {
842 error_setg(errp, "Failed to get time");
843 return -1;
844 }
845
846 if (!SystemTimeToFileTime(&ts, &tf)) {
847 error_setg(errp, "Failed to convert system time: %d", (int)GetLastError());
848 return -1;
849 }
850
851 time_ns = ((((int64_t)tf.dwHighDateTime << 32) | tf.dwLowDateTime)
852 - W32_FT_OFFSET) * 100;
853
854 return time_ns;
6912e6a9
LL
855}
856
2c958923 857void qmp_guest_set_time(bool has_time, int64_t time_ns, Error **errp)
a1bca57f 858{
0f230bf7 859 Error *local_err = NULL;
b8f954fe
LL
860 SYSTEMTIME ts;
861 FILETIME tf;
862 LONGLONG time;
863
ee17cbdc
MP
864 if (!has_time) {
865 /* Unfortunately, Windows libraries don't provide an easy way to access
866 * RTC yet:
867 *
868 * https://msdn.microsoft.com/en-us/library/aa908981.aspx
869 */
870 error_setg(errp, "Time argument is required on this platform");
871 return;
872 }
873
874 /* Validate time passed by user. */
875 if (time_ns < 0 || time_ns / 100 > INT64_MAX - W32_FT_OFFSET) {
876 error_setg(errp, "Time %" PRId64 "is invalid", time_ns);
877 return;
878 }
b8f954fe 879
ee17cbdc 880 time = time_ns / 100 + W32_FT_OFFSET;
b8f954fe 881
ee17cbdc
MP
882 tf.dwLowDateTime = (DWORD) time;
883 tf.dwHighDateTime = (DWORD) (time >> 32);
b8f954fe 884
ee17cbdc
MP
885 if (!FileTimeToSystemTime(&tf, &ts)) {
886 error_setg(errp, "Failed to convert system time %d",
887 (int)GetLastError());
888 return;
b8f954fe
LL
889 }
890
0f230bf7
MA
891 acquire_privilege(SE_SYSTEMTIME_NAME, &local_err);
892 if (local_err) {
893 error_propagate(errp, local_err);
b8f954fe
LL
894 return;
895 }
896
897 if (!SetSystemTime(&ts)) {
898 error_setg(errp, "Failed to set time to guest: %d", (int)GetLastError());
899 return;
900 }
a1bca57f
LL
901}
902
70e133a7
LE
903GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp)
904{
c6bd8c70 905 error_setg(errp, QERR_UNSUPPORTED);
70e133a7
LE
906 return NULL;
907}
908
909int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp)
910{
c6bd8c70 911 error_setg(errp, QERR_UNSUPPORTED);
70e133a7
LE
912 return -1;
913}
914
215a2771
DB
915void qmp_guest_set_user_password(const char *username,
916 const char *password,
917 bool crypted,
918 Error **errp)
919{
c6bd8c70 920 error_setg(errp, QERR_UNSUPPORTED);
215a2771
DB
921}
922
a065aaa9
HZ
923GuestMemoryBlockList *qmp_guest_get_memory_blocks(Error **errp)
924{
c6bd8c70 925 error_setg(errp, QERR_UNSUPPORTED);
a065aaa9
HZ
926 return NULL;
927}
928
929GuestMemoryBlockResponseList *
930qmp_guest_set_memory_blocks(GuestMemoryBlockList *mem_blks, Error **errp)
931{
c6bd8c70 932 error_setg(errp, QERR_UNSUPPORTED);
a065aaa9
HZ
933 return NULL;
934}
935
936GuestMemoryBlockInfo *qmp_guest_get_memory_block_info(Error **errp)
937{
c6bd8c70 938 error_setg(errp, QERR_UNSUPPORTED);
a065aaa9
HZ
939 return NULL;
940}
941
1281c08a
TS
942/* add unsupported commands to the blacklist */
943GList *ga_command_blacklist_init(GList *blacklist)
944{
945 const char *list_unsupported[] = {
d6c5528b 946 "guest-suspend-hybrid",
1281c08a 947 "guest-get-vcpus", "guest-set-vcpus",
215a2771 948 "guest-set-user-password",
0dd38a03
HZ
949 "guest-get-memory-blocks", "guest-set-memory-blocks",
950 "guest-get-memory-block-size",
ef0a03f2 951 "guest-fsfreeze-freeze-list",
1281c08a
TS
952 "guest-fstrim", NULL};
953 char **p = (char **)list_unsupported;
954
955 while (*p) {
956 blacklist = g_list_append(blacklist, *p++);
957 }
958
959 if (!vss_init(true)) {
c69403fc 960 g_debug("vss_init failed, vss commands are going to be disabled");
1281c08a
TS
961 const char *list[] = {
962 "guest-get-fsinfo", "guest-fsfreeze-status",
963 "guest-fsfreeze-freeze", "guest-fsfreeze-thaw", NULL};
964 p = (char **)list;
965
966 while (*p) {
967 blacklist = g_list_append(blacklist, *p++);
968 }
969 }
970
971 return blacklist;
972}
973
d8ca685a
MR
974/* register init/cleanup routines for stateful command groups */
975void ga_command_state_init(GAState *s, GACommandState *cs)
976{
1281c08a 977 if (!vss_initialized()) {
64c00317
TS
978 ga_command_state_add(cs, NULL, guest_fsfreeze_cleanup);
979 }
fa193594 980 ga_command_state_add(cs, guest_file_init, NULL);
d8ca685a 981}