]>
Commit | Line | Data |
---|---|---|
d8ca685a MR |
1 | /* |
2 | * QEMU Guest Agent win32-specific command implementations | |
3 | * | |
4 | * Copyright IBM Corp. 2012 | |
5 | * | |
6 | * Authors: | |
7 | * Michael Roth <[email protected]> | |
aa59637e | 8 | * Gal Hammer <[email protected]> |
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> | |
d8ca685a MR |
17 | #include "qga/guest-agent-core.h" |
18 | #include "qga-qmp-commands.h" | |
7b1b5d19 | 19 | #include "qapi/qmp/qerror.h" |
d8ca685a | 20 | |
546b60d0 MR |
21 | #ifndef SHTDN_REASON_FLAG_PLANNED |
22 | #define SHTDN_REASON_FLAG_PLANNED 0x80000000 | |
23 | #endif | |
24 | ||
3f2a6087 LL |
25 | /* multiple of 100 nanoseconds elapsed between windows baseline |
26 | * (1/1/1601) and Unix Epoch (1/1/1970), accounting for leap years */ | |
27 | #define W32_FT_OFFSET (10000000ULL * 60 * 60 * 24 * \ | |
28 | (365 * (1970 - 1601) + \ | |
29 | (1970 - 1601) / 4 - 3)) | |
30 | ||
aa59637e | 31 | static void acquire_privilege(const char *name, Error **err) |
d8ca685a | 32 | { |
546b60d0 MR |
33 | HANDLE token; |
34 | TOKEN_PRIVILEGES priv; | |
aa59637e GH |
35 | Error *local_err = NULL; |
36 | ||
37 | if (error_is_set(err)) { | |
38 | return; | |
39 | } | |
40 | ||
41 | if (OpenProcessToken(GetCurrentProcess(), | |
42 | TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, &token)) | |
43 | { | |
44 | if (!LookupPrivilegeValue(NULL, name, &priv.Privileges[0].Luid)) { | |
45 | error_set(&local_err, QERR_QGA_COMMAND_FAILED, | |
46 | "no luid for requested privilege"); | |
47 | goto out; | |
48 | } | |
49 | ||
50 | priv.PrivilegeCount = 1; | |
51 | priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; | |
52 | ||
53 | if (!AdjustTokenPrivileges(token, FALSE, &priv, 0, NULL, 0)) { | |
54 | error_set(&local_err, QERR_QGA_COMMAND_FAILED, | |
55 | "unable to acquire requested privilege"); | |
56 | goto out; | |
57 | } | |
58 | ||
59 | CloseHandle(token); | |
60 | } else { | |
61 | error_set(&local_err, QERR_QGA_COMMAND_FAILED, | |
62 | "failed to open privilege token"); | |
63 | } | |
64 | ||
65 | out: | |
66 | if (local_err) { | |
67 | error_propagate(err, local_err); | |
68 | } | |
69 | } | |
70 | ||
71 | static void execute_async(DWORD WINAPI (*func)(LPVOID), LPVOID opaque, Error **err) | |
72 | { | |
73 | Error *local_err = NULL; | |
74 | ||
75 | if (error_is_set(err)) { | |
76 | return; | |
77 | } | |
78 | HANDLE thread = CreateThread(NULL, 0, func, opaque, 0, NULL); | |
79 | if (!thread) { | |
80 | error_set(&local_err, QERR_QGA_COMMAND_FAILED, | |
81 | "failed to dispatch asynchronous command"); | |
82 | error_propagate(err, local_err); | |
83 | } | |
84 | } | |
85 | ||
86 | void qmp_guest_shutdown(bool has_mode, const char *mode, Error **err) | |
87 | { | |
546b60d0 MR |
88 | UINT shutdown_flag = EWX_FORCE; |
89 | ||
90 | slog("guest-shutdown called, mode: %s", mode); | |
91 | ||
92 | if (!has_mode || strcmp(mode, "powerdown") == 0) { | |
93 | shutdown_flag |= EWX_POWEROFF; | |
94 | } else if (strcmp(mode, "halt") == 0) { | |
95 | shutdown_flag |= EWX_SHUTDOWN; | |
96 | } else if (strcmp(mode, "reboot") == 0) { | |
97 | shutdown_flag |= EWX_REBOOT; | |
98 | } else { | |
99 | error_set(err, QERR_INVALID_PARAMETER_VALUE, "mode", | |
100 | "halt|powerdown|reboot"); | |
101 | return; | |
102 | } | |
103 | ||
104 | /* Request a shutdown privilege, but try to shut down the system | |
105 | anyway. */ | |
aa59637e GH |
106 | acquire_privilege(SE_SHUTDOWN_NAME, err); |
107 | if (error_is_set(err)) { | |
108 | return; | |
546b60d0 MR |
109 | } |
110 | ||
111 | if (!ExitWindowsEx(shutdown_flag, SHTDN_REASON_FLAG_PLANNED)) { | |
112 | slog("guest-shutdown failed: %d", GetLastError()); | |
113 | error_set(err, QERR_UNDEFINED_ERROR); | |
114 | } | |
d8ca685a MR |
115 | } |
116 | ||
117 | int64_t qmp_guest_file_open(const char *path, bool has_mode, const char *mode, Error **err) | |
118 | { | |
119 | error_set(err, QERR_UNSUPPORTED); | |
120 | return 0; | |
121 | } | |
122 | ||
123 | void qmp_guest_file_close(int64_t handle, Error **err) | |
124 | { | |
125 | error_set(err, QERR_UNSUPPORTED); | |
126 | } | |
127 | ||
128 | GuestFileRead *qmp_guest_file_read(int64_t handle, bool has_count, | |
129 | int64_t count, Error **err) | |
130 | { | |
131 | error_set(err, QERR_UNSUPPORTED); | |
132 | return 0; | |
133 | } | |
134 | ||
135 | GuestFileWrite *qmp_guest_file_write(int64_t handle, const char *buf_b64, | |
136 | bool has_count, int64_t count, Error **err) | |
137 | { | |
138 | error_set(err, QERR_UNSUPPORTED); | |
139 | return 0; | |
140 | } | |
141 | ||
142 | GuestFileSeek *qmp_guest_file_seek(int64_t handle, int64_t offset, | |
143 | int64_t whence, Error **err) | |
144 | { | |
145 | error_set(err, QERR_UNSUPPORTED); | |
146 | return 0; | |
147 | } | |
148 | ||
149 | void qmp_guest_file_flush(int64_t handle, Error **err) | |
150 | { | |
151 | error_set(err, QERR_UNSUPPORTED); | |
152 | } | |
153 | ||
154 | /* | |
155 | * Return status of freeze/thaw | |
156 | */ | |
157 | GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **err) | |
158 | { | |
159 | error_set(err, QERR_UNSUPPORTED); | |
160 | return 0; | |
161 | } | |
162 | ||
163 | /* | |
164 | * Walk list of mounted file systems in the guest, and freeze the ones which | |
165 | * are real local file systems. | |
166 | */ | |
167 | int64_t qmp_guest_fsfreeze_freeze(Error **err) | |
168 | { | |
169 | error_set(err, QERR_UNSUPPORTED); | |
170 | return 0; | |
171 | } | |
172 | ||
173 | /* | |
174 | * Walk list of frozen file systems in the guest, and thaw them. | |
175 | */ | |
176 | int64_t qmp_guest_fsfreeze_thaw(Error **err) | |
177 | { | |
178 | error_set(err, QERR_UNSUPPORTED); | |
179 | return 0; | |
180 | } | |
181 | ||
eab5fd59 PB |
182 | /* |
183 | * Walk list of mounted file systems in the guest, and discard unused | |
184 | * areas. | |
185 | */ | |
186 | void qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **err) | |
187 | { | |
188 | error_set(err, QERR_UNSUPPORTED); | |
eab5fd59 PB |
189 | } |
190 | ||
aa59637e | 191 | typedef enum { |
f54603b6 MR |
192 | GUEST_SUSPEND_MODE_DISK, |
193 | GUEST_SUSPEND_MODE_RAM | |
aa59637e GH |
194 | } GuestSuspendMode; |
195 | ||
196 | static void check_suspend_mode(GuestSuspendMode mode, Error **err) | |
197 | { | |
198 | SYSTEM_POWER_CAPABILITIES sys_pwr_caps; | |
199 | Error *local_err = NULL; | |
200 | ||
201 | if (error_is_set(err)) { | |
202 | return; | |
203 | } | |
204 | ZeroMemory(&sys_pwr_caps, sizeof(sys_pwr_caps)); | |
205 | if (!GetPwrCapabilities(&sys_pwr_caps)) { | |
206 | error_set(&local_err, QERR_QGA_COMMAND_FAILED, | |
207 | "failed to determine guest suspend capabilities"); | |
208 | goto out; | |
209 | } | |
210 | ||
f54603b6 MR |
211 | switch (mode) { |
212 | case GUEST_SUSPEND_MODE_DISK: | |
213 | if (!sys_pwr_caps.SystemS4) { | |
214 | error_set(&local_err, QERR_QGA_COMMAND_FAILED, | |
215 | "suspend-to-disk not supported by OS"); | |
aa59637e | 216 | } |
f54603b6 MR |
217 | break; |
218 | case GUEST_SUSPEND_MODE_RAM: | |
219 | if (!sys_pwr_caps.SystemS3) { | |
220 | error_set(&local_err, QERR_QGA_COMMAND_FAILED, | |
221 | "suspend-to-ram not supported by OS"); | |
222 | } | |
223 | break; | |
224 | default: | |
aa59637e GH |
225 | error_set(&local_err, QERR_INVALID_PARAMETER_VALUE, "mode", |
226 | "GuestSuspendMode"); | |
aa59637e GH |
227 | } |
228 | ||
aa59637e GH |
229 | out: |
230 | if (local_err) { | |
231 | error_propagate(err, local_err); | |
232 | } | |
233 | } | |
234 | ||
235 | static DWORD WINAPI do_suspend(LPVOID opaque) | |
236 | { | |
237 | GuestSuspendMode *mode = opaque; | |
238 | DWORD ret = 0; | |
239 | ||
240 | if (!SetSuspendState(*mode == GUEST_SUSPEND_MODE_DISK, TRUE, TRUE)) { | |
241 | slog("failed to suspend guest, %s", GetLastError()); | |
242 | ret = -1; | |
243 | } | |
244 | g_free(mode); | |
245 | return ret; | |
246 | } | |
247 | ||
11d0f125 LC |
248 | void qmp_guest_suspend_disk(Error **err) |
249 | { | |
aa59637e GH |
250 | GuestSuspendMode *mode = g_malloc(sizeof(GuestSuspendMode)); |
251 | ||
252 | *mode = GUEST_SUSPEND_MODE_DISK; | |
253 | check_suspend_mode(*mode, err); | |
254 | acquire_privilege(SE_SHUTDOWN_NAME, err); | |
255 | execute_async(do_suspend, mode, err); | |
256 | ||
257 | if (error_is_set(err)) { | |
258 | g_free(mode); | |
259 | } | |
11d0f125 LC |
260 | } |
261 | ||
fbf42210 LC |
262 | void qmp_guest_suspend_ram(Error **err) |
263 | { | |
f54603b6 MR |
264 | GuestSuspendMode *mode = g_malloc(sizeof(GuestSuspendMode)); |
265 | ||
266 | *mode = GUEST_SUSPEND_MODE_RAM; | |
267 | check_suspend_mode(*mode, err); | |
268 | acquire_privilege(SE_SHUTDOWN_NAME, err); | |
269 | execute_async(do_suspend, mode, err); | |
270 | ||
271 | if (error_is_set(err)) { | |
272 | g_free(mode); | |
273 | } | |
fbf42210 LC |
274 | } |
275 | ||
95f4f404 LC |
276 | void qmp_guest_suspend_hybrid(Error **err) |
277 | { | |
278 | error_set(err, QERR_UNSUPPORTED); | |
279 | } | |
280 | ||
3424fc9f MP |
281 | GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **err) |
282 | { | |
283 | error_set(err, QERR_UNSUPPORTED); | |
284 | return NULL; | |
285 | } | |
286 | ||
6912e6a9 LL |
287 | int64_t qmp_guest_get_time(Error **errp) |
288 | { | |
3f2a6087 LL |
289 | SYSTEMTIME ts = {0}; |
290 | int64_t time_ns; | |
291 | FILETIME tf; | |
292 | ||
293 | GetSystemTime(&ts); | |
294 | if (ts.wYear < 1601 || ts.wYear > 30827) { | |
295 | error_setg(errp, "Failed to get time"); | |
296 | return -1; | |
297 | } | |
298 | ||
299 | if (!SystemTimeToFileTime(&ts, &tf)) { | |
300 | error_setg(errp, "Failed to convert system time: %d", (int)GetLastError()); | |
301 | return -1; | |
302 | } | |
303 | ||
304 | time_ns = ((((int64_t)tf.dwHighDateTime << 32) | tf.dwLowDateTime) | |
305 | - W32_FT_OFFSET) * 100; | |
306 | ||
307 | return time_ns; | |
6912e6a9 LL |
308 | } |
309 | ||
a1bca57f LL |
310 | void qmp_guest_set_time(int64_t time_ns, Error **errp) |
311 | { | |
b8f954fe LL |
312 | SYSTEMTIME ts; |
313 | FILETIME tf; | |
314 | LONGLONG time; | |
315 | ||
316 | if (time_ns < 0 || time_ns / 100 > INT64_MAX - W32_FT_OFFSET) { | |
317 | error_setg(errp, "Time %" PRId64 "is invalid", time_ns); | |
318 | return; | |
319 | } | |
320 | ||
321 | time = time_ns / 100 + W32_FT_OFFSET; | |
322 | ||
323 | tf.dwLowDateTime = (DWORD) time; | |
324 | tf.dwHighDateTime = (DWORD) (time >> 32); | |
325 | ||
326 | if (!FileTimeToSystemTime(&tf, &ts)) { | |
327 | error_setg(errp, "Failed to convert system time %d", (int)GetLastError()); | |
328 | return; | |
329 | } | |
330 | ||
331 | acquire_privilege(SE_SYSTEMTIME_NAME, errp); | |
332 | if (error_is_set(errp)) { | |
333 | return; | |
334 | } | |
335 | ||
336 | if (!SetSystemTime(&ts)) { | |
337 | error_setg(errp, "Failed to set time to guest: %d", (int)GetLastError()); | |
338 | return; | |
339 | } | |
a1bca57f LL |
340 | } |
341 | ||
70e133a7 LE |
342 | GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp) |
343 | { | |
344 | error_set(errp, QERR_UNSUPPORTED); | |
345 | return NULL; | |
346 | } | |
347 | ||
348 | int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp) | |
349 | { | |
350 | error_set(errp, QERR_UNSUPPORTED); | |
351 | return -1; | |
352 | } | |
353 | ||
d8ca685a MR |
354 | /* register init/cleanup routines for stateful command groups */ |
355 | void ga_command_state_init(GAState *s, GACommandState *cs) | |
356 | { | |
357 | } |