2 * QEMU Guest Agent win32-specific command implementations
4 * Copyright IBM Corp. 2012
7 * Michael Roth <mdroth@linux.vnet.ibm.com>
8 * Gal Hammer <ghammer@redhat.com>
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.
19 #include "qga/guest-agent-core.h"
20 #include "qga/vss-win32.h"
21 #include "qga-qmp-commands.h"
22 #include "qapi/qmp/qerror.h"
23 #include "qemu/queue.h"
25 #ifndef SHTDN_REASON_FLAG_PLANNED
26 #define SHTDN_REASON_FLAG_PLANNED 0x80000000
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))
35 #define INVALID_SET_FILE_POINTER ((DWORD)-1)
37 typedef struct GuestFileHandle
{
40 QTAILQ_ENTRY(GuestFileHandle
) next
;
44 QTAILQ_HEAD(, GuestFileHandle
) filehandles
;
48 typedef struct OpenFlags
{
51 DWORD creation_disposition
;
53 static 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
}
70 static OpenFlags
*find_open_flag(const char *mode_str
)
75 for (mode
= 0; mode
< ARRAY_SIZE(guest_file_open_modes
); ++mode
) {
76 OpenFlags
*flags
= guest_file_open_modes
+ mode
;
78 if (strcmp(flags
->forms
, mode_str
) == 0) {
83 error_setg(errp
, "invalid file open mode '%s'", mode_str
);
87 static int64_t guest_file_handle_add(HANDLE fh
, Error
**errp
)
92 handle
= ga_get_fd_handle(ga_state
, errp
);
96 gfh
= g_malloc0(sizeof(GuestFileHandle
));
99 QTAILQ_INSERT_TAIL(&guest_file_state
.filehandles
, gfh
, next
);
104 static GuestFileHandle
*guest_file_handle_find(int64_t id
, Error
**errp
)
106 GuestFileHandle
*gfh
;
107 QTAILQ_FOREACH(gfh
, &guest_file_state
.filehandles
, next
) {
112 error_setg(errp
, "handle '%" PRId64
"' has not been found", id
);
116 int64_t qmp_guest_file_open(const char *path
, bool has_mode
,
117 const char *mode
, Error
**errp
)
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
;
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");
137 fh
= CreateFile(path
, guest_flags
->desired_access
, share_mode
, sa_attr
,
138 guest_flags
->creation_disposition
, flags_and_attr
,
140 if (fh
== INVALID_HANDLE_VALUE
) {
141 error_setg_win32(errp
, GetLastError(), "failed to open file '%s'",
146 fd
= guest_file_handle_add(fh
, errp
);
149 error_setg(errp
, "failed to add handle to qmp handle table");
153 slog("guest-file-open, handle: % " PRId64
, fd
);
157 void qmp_guest_file_close(int64_t handle
, Error
**errp
)
160 GuestFileHandle
*gfh
= guest_file_handle_find(handle
, errp
);
161 slog("guest-file-close called, handle: %" PRId64
, handle
);
165 ret
= CloseHandle(gfh
->fh
);
167 error_setg_win32(errp
, GetLastError(), "failed close handle");
171 QTAILQ_REMOVE(&guest_file_state
.filehandles
, gfh
, next
);
175 static void acquire_privilege(const char *name
, Error
**errp
)
178 TOKEN_PRIVILEGES priv
;
179 Error
*local_err
= NULL
;
181 if (OpenProcessToken(GetCurrentProcess(),
182 TOKEN_ADJUST_PRIVILEGES
|TOKEN_QUERY
, &token
))
184 if (!LookupPrivilegeValue(NULL
, name
, &priv
.Privileges
[0].Luid
)) {
185 error_set(&local_err
, QERR_QGA_COMMAND_FAILED
,
186 "no luid for requested privilege");
190 priv
.PrivilegeCount
= 1;
191 priv
.Privileges
[0].Attributes
= SE_PRIVILEGE_ENABLED
;
193 if (!AdjustTokenPrivileges(token
, FALSE
, &priv
, 0, NULL
, 0)) {
194 error_set(&local_err
, QERR_QGA_COMMAND_FAILED
,
195 "unable to acquire requested privilege");
200 error_set(&local_err
, QERR_QGA_COMMAND_FAILED
,
201 "failed to open privilege token");
209 error_propagate(errp
, local_err
);
213 static void execute_async(DWORD
WINAPI (*func
)(LPVOID
), LPVOID opaque
,
216 Error
*local_err
= NULL
;
218 HANDLE thread
= CreateThread(NULL
, 0, func
, opaque
, 0, NULL
);
220 error_set(&local_err
, QERR_QGA_COMMAND_FAILED
,
221 "failed to dispatch asynchronous command");
222 error_propagate(errp
, local_err
);
226 void qmp_guest_shutdown(bool has_mode
, const char *mode
, Error
**errp
)
228 Error
*local_err
= NULL
;
229 UINT shutdown_flag
= EWX_FORCE
;
231 slog("guest-shutdown called, mode: %s", mode
);
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
;
240 error_set(errp
, QERR_INVALID_PARAMETER_VALUE
, "mode",
241 "halt|powerdown|reboot");
245 /* Request a shutdown privilege, but try to shut down the system
247 acquire_privilege(SE_SHUTDOWN_NAME
, &local_err
);
249 error_propagate(errp
, local_err
);
253 if (!ExitWindowsEx(shutdown_flag
, SHTDN_REASON_FLAG_PLANNED
)) {
254 slog("guest-shutdown failed: %lu", GetLastError());
255 error_set(errp
, QERR_UNDEFINED_ERROR
);
259 GuestFileRead
*qmp_guest_file_read(int64_t handle
, bool has_count
,
260 int64_t count
, Error
**errp
)
262 GuestFileRead
*read_data
= NULL
;
267 GuestFileHandle
*gfh
= guest_file_handle_find(handle
, errp
);
273 count
= QGA_READ_COUNT_DEFAULT
;
274 } else if (count
< 0) {
275 error_setg(errp
, "value '%" PRId64
276 "' is invalid for argument count", count
);
281 buf
= g_malloc0(count
+1);
282 is_ok
= ReadFile(fh
, buf
, count
, &read_count
, NULL
);
284 error_setg_win32(errp
, GetLastError(), "failed to read file");
285 slog("guest-file-read failed, handle %" PRId64
, handle
);
288 read_data
= g_malloc0(sizeof(GuestFileRead
));
289 read_data
->count
= (size_t)read_count
;
290 read_data
->eof
= read_count
== 0;
292 if (read_count
!= 0) {
293 read_data
->buf_b64
= g_base64_encode(buf
, read_count
);
301 GuestFileWrite
*qmp_guest_file_write(int64_t handle
, const char *buf_b64
,
302 bool has_count
, int64_t count
,
305 GuestFileWrite
*write_data
= NULL
;
310 GuestFileHandle
*gfh
= guest_file_handle_find(handle
, errp
);
317 buf
= g_base64_decode(buf_b64
, &buf_len
);
321 } else if (count
< 0 || count
> buf_len
) {
322 error_setg(errp
, "value '%" PRId64
323 "' is invalid for argument count", count
);
327 is_ok
= WriteFile(fh
, buf
, count
, &write_count
, NULL
);
329 error_setg_win32(errp
, GetLastError(), "failed to write to file");
330 slog("guest-file-write-failed, handle: %" PRId64
, handle
);
332 write_data
= g_malloc0(sizeof(GuestFileWrite
));
333 write_data
->count
= (size_t) write_count
;
341 GuestFileSeek
*qmp_guest_file_seek(int64_t handle
, int64_t offset
,
342 int64_t whence
, Error
**errp
)
344 GuestFileHandle
*gfh
;
345 GuestFileSeek
*seek_data
;
347 LARGE_INTEGER new_pos
, off_pos
;
348 off_pos
.QuadPart
= offset
;
350 gfh
= guest_file_handle_find(handle
, errp
);
356 res
= SetFilePointerEx(fh
, off_pos
, &new_pos
, whence
);
358 error_setg_win32(errp
, GetLastError(), "failed to seek file");
361 seek_data
= g_new0(GuestFileSeek
, 1);
362 seek_data
->position
= new_pos
.QuadPart
;
366 void qmp_guest_file_flush(int64_t handle
, Error
**errp
)
369 GuestFileHandle
*gfh
= guest_file_handle_find(handle
, errp
);
375 if (!FlushFileBuffers(fh
)) {
376 error_setg_win32(errp
, GetLastError(), "failed to flush file");
380 static void guest_file_init(void)
382 QTAILQ_INIT(&guest_file_state
.filehandles
);
385 GuestFilesystemInfoList
*qmp_guest_get_fsinfo(Error
**errp
)
387 error_set(errp
, QERR_UNSUPPORTED
);
392 * Return status of freeze/thaw
394 GuestFsfreezeStatus
qmp_guest_fsfreeze_status(Error
**errp
)
396 if (!vss_initialized()) {
397 error_set(errp
, QERR_UNSUPPORTED
);
401 if (ga_is_frozen(ga_state
)) {
402 return GUEST_FSFREEZE_STATUS_FROZEN
;
405 return GUEST_FSFREEZE_STATUS_THAWED
;
409 * Freeze local file systems using Volume Shadow-copy Service.
410 * The frozen state is limited for up to 10 seconds by VSS.
412 int64_t qmp_guest_fsfreeze_freeze(Error
**errp
)
415 Error
*local_err
= NULL
;
417 if (!vss_initialized()) {
418 error_set(errp
, QERR_UNSUPPORTED
);
422 slog("guest-fsfreeze called");
424 /* cannot risk guest agent blocking itself on a write in this state */
425 ga_set_frozen(ga_state
);
427 qga_vss_fsfreeze(&i
, &local_err
, true);
429 error_propagate(errp
, local_err
);
437 qmp_guest_fsfreeze_thaw(&local_err
);
439 g_debug("cleanup thaw: %s", error_get_pretty(local_err
));
440 error_free(local_err
);
445 int64_t qmp_guest_fsfreeze_freeze_list(bool has_mountpoints
,
446 strList
*mountpoints
,
449 error_set(errp
, QERR_UNSUPPORTED
);
455 * Thaw local file systems using Volume Shadow-copy Service.
457 int64_t qmp_guest_fsfreeze_thaw(Error
**errp
)
461 if (!vss_initialized()) {
462 error_set(errp
, QERR_UNSUPPORTED
);
466 qga_vss_fsfreeze(&i
, errp
, false);
468 ga_unset_frozen(ga_state
);
472 static void guest_fsfreeze_cleanup(void)
476 if (!vss_initialized()) {
480 if (ga_is_frozen(ga_state
) == GUEST_FSFREEZE_STATUS_FROZEN
) {
481 qmp_guest_fsfreeze_thaw(&err
);
483 slog("failed to clean up frozen filesystems: %s",
484 error_get_pretty(err
));
493 * Walk list of mounted file systems in the guest, and discard unused
496 void qmp_guest_fstrim(bool has_minimum
, int64_t minimum
, Error
**errp
)
498 error_set(errp
, QERR_UNSUPPORTED
);
502 GUEST_SUSPEND_MODE_DISK
,
503 GUEST_SUSPEND_MODE_RAM
506 static void check_suspend_mode(GuestSuspendMode mode
, Error
**errp
)
508 SYSTEM_POWER_CAPABILITIES sys_pwr_caps
;
509 Error
*local_err
= NULL
;
511 ZeroMemory(&sys_pwr_caps
, sizeof(sys_pwr_caps
));
512 if (!GetPwrCapabilities(&sys_pwr_caps
)) {
513 error_set(&local_err
, QERR_QGA_COMMAND_FAILED
,
514 "failed to determine guest suspend capabilities");
519 case GUEST_SUSPEND_MODE_DISK
:
520 if (!sys_pwr_caps
.SystemS4
) {
521 error_set(&local_err
, QERR_QGA_COMMAND_FAILED
,
522 "suspend-to-disk not supported by OS");
525 case GUEST_SUSPEND_MODE_RAM
:
526 if (!sys_pwr_caps
.SystemS3
) {
527 error_set(&local_err
, QERR_QGA_COMMAND_FAILED
,
528 "suspend-to-ram not supported by OS");
532 error_set(&local_err
, QERR_INVALID_PARAMETER_VALUE
, "mode",
538 error_propagate(errp
, local_err
);
542 static DWORD WINAPI
do_suspend(LPVOID opaque
)
544 GuestSuspendMode
*mode
= opaque
;
547 if (!SetSuspendState(*mode
== GUEST_SUSPEND_MODE_DISK
, TRUE
, TRUE
)) {
548 slog("failed to suspend guest, %lu", GetLastError());
555 void qmp_guest_suspend_disk(Error
**errp
)
557 Error
*local_err
= NULL
;
558 GuestSuspendMode
*mode
= g_malloc(sizeof(GuestSuspendMode
));
560 *mode
= GUEST_SUSPEND_MODE_DISK
;
561 check_suspend_mode(*mode
, &local_err
);
562 acquire_privilege(SE_SHUTDOWN_NAME
, &local_err
);
563 execute_async(do_suspend
, mode
, &local_err
);
566 error_propagate(errp
, local_err
);
571 void qmp_guest_suspend_ram(Error
**errp
)
573 Error
*local_err
= NULL
;
574 GuestSuspendMode
*mode
= g_malloc(sizeof(GuestSuspendMode
));
576 *mode
= GUEST_SUSPEND_MODE_RAM
;
577 check_suspend_mode(*mode
, &local_err
);
578 acquire_privilege(SE_SHUTDOWN_NAME
, &local_err
);
579 execute_async(do_suspend
, mode
, &local_err
);
582 error_propagate(errp
, local_err
);
587 void qmp_guest_suspend_hybrid(Error
**errp
)
589 error_set(errp
, QERR_UNSUPPORTED
);
592 GuestNetworkInterfaceList
*qmp_guest_network_get_interfaces(Error
**errp
)
594 error_set(errp
, QERR_UNSUPPORTED
);
598 int64_t qmp_guest_get_time(Error
**errp
)
605 if (ts
.wYear
< 1601 || ts
.wYear
> 30827) {
606 error_setg(errp
, "Failed to get time");
610 if (!SystemTimeToFileTime(&ts
, &tf
)) {
611 error_setg(errp
, "Failed to convert system time: %d", (int)GetLastError());
615 time_ns
= ((((int64_t)tf
.dwHighDateTime
<< 32) | tf
.dwLowDateTime
)
616 - W32_FT_OFFSET
) * 100;
621 void qmp_guest_set_time(bool has_time
, int64_t time_ns
, Error
**errp
)
623 Error
*local_err
= NULL
;
629 /* Unfortunately, Windows libraries don't provide an easy way to access
632 * https://msdn.microsoft.com/en-us/library/aa908981.aspx
634 error_setg(errp
, "Time argument is required on this platform");
638 /* Validate time passed by user. */
639 if (time_ns
< 0 || time_ns
/ 100 > INT64_MAX
- W32_FT_OFFSET
) {
640 error_setg(errp
, "Time %" PRId64
"is invalid", time_ns
);
644 time
= time_ns
/ 100 + W32_FT_OFFSET
;
646 tf
.dwLowDateTime
= (DWORD
) time
;
647 tf
.dwHighDateTime
= (DWORD
) (time
>> 32);
649 if (!FileTimeToSystemTime(&tf
, &ts
)) {
650 error_setg(errp
, "Failed to convert system time %d",
651 (int)GetLastError());
655 acquire_privilege(SE_SYSTEMTIME_NAME
, &local_err
);
657 error_propagate(errp
, local_err
);
661 if (!SetSystemTime(&ts
)) {
662 error_setg(errp
, "Failed to set time to guest: %d", (int)GetLastError());
667 GuestLogicalProcessorList
*qmp_guest_get_vcpus(Error
**errp
)
669 error_set(errp
, QERR_UNSUPPORTED
);
673 int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList
*vcpus
, Error
**errp
)
675 error_set(errp
, QERR_UNSUPPORTED
);
679 void qmp_guest_set_user_password(const char *username
,
680 const char *password
,
684 error_set(errp
, QERR_UNSUPPORTED
);
687 GuestMemoryBlockList
*qmp_guest_get_memory_blocks(Error
**errp
)
689 error_set(errp
, QERR_UNSUPPORTED
);
693 GuestMemoryBlockResponseList
*
694 qmp_guest_set_memory_blocks(GuestMemoryBlockList
*mem_blks
, Error
**errp
)
696 error_set(errp
, QERR_UNSUPPORTED
);
700 GuestMemoryBlockInfo
*qmp_guest_get_memory_block_info(Error
**errp
)
702 error_set(errp
, QERR_UNSUPPORTED
);
706 /* add unsupported commands to the blacklist */
707 GList
*ga_command_blacklist_init(GList
*blacklist
)
709 const char *list_unsupported
[] = {
710 "guest-suspend-hybrid", "guest-network-get-interfaces",
711 "guest-get-vcpus", "guest-set-vcpus",
712 "guest-set-user-password",
713 "guest-get-memory-blocks", "guest-set-memory-blocks",
714 "guest-get-memory-block-size",
715 "guest-fsfreeze-freeze-list", "guest-get-fsinfo",
716 "guest-fstrim", NULL
};
717 char **p
= (char **)list_unsupported
;
720 blacklist
= g_list_append(blacklist
, *p
++);
723 if (!vss_init(true)) {
724 const char *list
[] = {
725 "guest-get-fsinfo", "guest-fsfreeze-status",
726 "guest-fsfreeze-freeze", "guest-fsfreeze-thaw", NULL
};
730 blacklist
= g_list_append(blacklist
, *p
++);
737 /* register init/cleanup routines for stateful command groups */
738 void ga_command_state_init(GAState
*s
, GACommandState
*cs
)
740 if (!vss_initialized()) {
741 ga_command_state_add(cs
, NULL
, guest_fsfreeze_cleanup
);
743 ga_command_state_add(cs
, guest_file_init
, NULL
);