]> Git Repo - qemu.git/blob - qga/commands-win32.c
qga: Consistently name Error ** objects errp, and not err
[qemu.git] / qga / commands-win32.c
1 /*
2  * QEMU Guest Agent win32-specific command implementations
3  *
4  * Copyright IBM Corp. 2012
5  *
6  * Authors:
7  *  Michael Roth      <[email protected]>
8  *  Gal Hammer        <[email protected]>
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 **errp)
33 {
34     HANDLE token;
35     TOKEN_PRIVILEGES priv;
36     Error *local_err = NULL;
37
38     if (error_is_set(errp)) {
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(errp, local_err);
69     }
70 }
71
72 static void execute_async(DWORD WINAPI (*func)(LPVOID), LPVOID opaque,
73                           Error **errp)
74 {
75     Error *local_err = NULL;
76
77     if (error_is_set(errp)) {
78         return;
79     }
80     HANDLE thread = CreateThread(NULL, 0, func, opaque, 0, NULL);
81     if (!thread) {
82         error_set(&local_err, QERR_QGA_COMMAND_FAILED,
83                   "failed to dispatch asynchronous command");
84         error_propagate(errp, local_err);
85     }
86 }
87
88 void qmp_guest_shutdown(bool has_mode, const char *mode, Error **errp)
89 {
90     UINT shutdown_flag = EWX_FORCE;
91
92     slog("guest-shutdown called, mode: %s", mode);
93
94     if (!has_mode || strcmp(mode, "powerdown") == 0) {
95         shutdown_flag |= EWX_POWEROFF;
96     } else if (strcmp(mode, "halt") == 0) {
97         shutdown_flag |= EWX_SHUTDOWN;
98     } else if (strcmp(mode, "reboot") == 0) {
99         shutdown_flag |= EWX_REBOOT;
100     } else {
101         error_set(errp, QERR_INVALID_PARAMETER_VALUE, "mode",
102                   "halt|powerdown|reboot");
103         return;
104     }
105
106     /* Request a shutdown privilege, but try to shut down the system
107        anyway. */
108     acquire_privilege(SE_SHUTDOWN_NAME, errp);
109     if (error_is_set(errp)) {
110         return;
111     }
112
113     if (!ExitWindowsEx(shutdown_flag, SHTDN_REASON_FLAG_PLANNED)) {
114         slog("guest-shutdown failed: %lu", GetLastError());
115         error_set(errp, QERR_UNDEFINED_ERROR);
116     }
117 }
118
119 int64_t qmp_guest_file_open(const char *path, bool has_mode, const char *mode,
120                             Error **errp)
121 {
122     error_set(errp, QERR_UNSUPPORTED);
123     return 0;
124 }
125
126 void qmp_guest_file_close(int64_t handle, Error **errp)
127 {
128     error_set(errp, QERR_UNSUPPORTED);
129 }
130
131 GuestFileRead *qmp_guest_file_read(int64_t handle, bool has_count,
132                                    int64_t count, Error **errp)
133 {
134     error_set(errp, QERR_UNSUPPORTED);
135     return 0;
136 }
137
138 GuestFileWrite *qmp_guest_file_write(int64_t handle, const char *buf_b64,
139                                      bool has_count, int64_t count,
140                                      Error **errp)
141 {
142     error_set(errp, QERR_UNSUPPORTED);
143     return 0;
144 }
145
146 GuestFileSeek *qmp_guest_file_seek(int64_t handle, int64_t offset,
147                                    int64_t whence, Error **errp)
148 {
149     error_set(errp, QERR_UNSUPPORTED);
150     return 0;
151 }
152
153 void qmp_guest_file_flush(int64_t handle, Error **errp)
154 {
155     error_set(errp, QERR_UNSUPPORTED);
156 }
157
158 /*
159  * Return status of freeze/thaw
160  */
161 GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **errp)
162 {
163     if (!vss_initialized()) {
164         error_set(errp, QERR_UNSUPPORTED);
165         return 0;
166     }
167
168     if (ga_is_frozen(ga_state)) {
169         return GUEST_FSFREEZE_STATUS_FROZEN;
170     }
171
172     return GUEST_FSFREEZE_STATUS_THAWED;
173 }
174
175 /*
176  * Freeze local file systems using Volume Shadow-copy Service.
177  * The frozen state is limited for up to 10 seconds by VSS.
178  */
179 int64_t qmp_guest_fsfreeze_freeze(Error **errp)
180 {
181     int i;
182     Error *local_err = NULL;
183
184     if (!vss_initialized()) {
185         error_set(errp, QERR_UNSUPPORTED);
186         return 0;
187     }
188
189     slog("guest-fsfreeze called");
190
191     /* cannot risk guest agent blocking itself on a write in this state */
192     ga_set_frozen(ga_state);
193
194     qga_vss_fsfreeze(&i, errp, true);
195     if (error_is_set(errp)) {
196         goto error;
197     }
198
199     return i;
200
201 error:
202     qmp_guest_fsfreeze_thaw(&local_err);
203     if (local_err) {
204         g_debug("cleanup thaw: %s", error_get_pretty(local_err));
205         error_free(local_err);
206     }
207     return 0;
208 }
209
210 /*
211  * Thaw local file systems using Volume Shadow-copy Service.
212  */
213 int64_t qmp_guest_fsfreeze_thaw(Error **errp)
214 {
215     int i;
216
217     if (!vss_initialized()) {
218         error_set(errp, QERR_UNSUPPORTED);
219         return 0;
220     }
221
222     qga_vss_fsfreeze(&i, errp, false);
223
224     ga_unset_frozen(ga_state);
225     return i;
226 }
227
228 static void guest_fsfreeze_cleanup(void)
229 {
230     Error *err = NULL;
231
232     if (!vss_initialized()) {
233         return;
234     }
235
236     if (ga_is_frozen(ga_state) == GUEST_FSFREEZE_STATUS_FROZEN) {
237         qmp_guest_fsfreeze_thaw(&err);
238         if (err) {
239             slog("failed to clean up frozen filesystems: %s",
240                  error_get_pretty(err));
241             error_free(err);
242         }
243     }
244
245     vss_deinit(true);
246 }
247
248 /*
249  * Walk list of mounted file systems in the guest, and discard unused
250  * areas.
251  */
252 void qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp)
253 {
254     error_set(errp, QERR_UNSUPPORTED);
255 }
256
257 typedef enum {
258     GUEST_SUSPEND_MODE_DISK,
259     GUEST_SUSPEND_MODE_RAM
260 } GuestSuspendMode;
261
262 static void check_suspend_mode(GuestSuspendMode mode, Error **errp)
263 {
264     SYSTEM_POWER_CAPABILITIES sys_pwr_caps;
265     Error *local_err = NULL;
266
267     if (error_is_set(errp)) {
268         return;
269     }
270     ZeroMemory(&sys_pwr_caps, sizeof(sys_pwr_caps));
271     if (!GetPwrCapabilities(&sys_pwr_caps)) {
272         error_set(&local_err, QERR_QGA_COMMAND_FAILED,
273                   "failed to determine guest suspend capabilities");
274         goto out;
275     }
276
277     switch (mode) {
278     case GUEST_SUSPEND_MODE_DISK:
279         if (!sys_pwr_caps.SystemS4) {
280             error_set(&local_err, QERR_QGA_COMMAND_FAILED,
281                       "suspend-to-disk not supported by OS");
282         }
283         break;
284     case GUEST_SUSPEND_MODE_RAM:
285         if (!sys_pwr_caps.SystemS3) {
286             error_set(&local_err, QERR_QGA_COMMAND_FAILED,
287                       "suspend-to-ram not supported by OS");
288         }
289         break;
290     default:
291         error_set(&local_err, QERR_INVALID_PARAMETER_VALUE, "mode",
292                   "GuestSuspendMode");
293     }
294
295 out:
296     if (local_err) {
297         error_propagate(errp, local_err);
298     }
299 }
300
301 static DWORD WINAPI do_suspend(LPVOID opaque)
302 {
303     GuestSuspendMode *mode = opaque;
304     DWORD ret = 0;
305
306     if (!SetSuspendState(*mode == GUEST_SUSPEND_MODE_DISK, TRUE, TRUE)) {
307         slog("failed to suspend guest, %lu", GetLastError());
308         ret = -1;
309     }
310     g_free(mode);
311     return ret;
312 }
313
314 void qmp_guest_suspend_disk(Error **errp)
315 {
316     GuestSuspendMode *mode = g_malloc(sizeof(GuestSuspendMode));
317
318     *mode = GUEST_SUSPEND_MODE_DISK;
319     check_suspend_mode(*mode, errp);
320     acquire_privilege(SE_SHUTDOWN_NAME, errp);
321     execute_async(do_suspend, mode, errp);
322
323     if (error_is_set(errp)) {
324         g_free(mode);
325     }
326 }
327
328 void qmp_guest_suspend_ram(Error **errp)
329 {
330     GuestSuspendMode *mode = g_malloc(sizeof(GuestSuspendMode));
331
332     *mode = GUEST_SUSPEND_MODE_RAM;
333     check_suspend_mode(*mode, errp);
334     acquire_privilege(SE_SHUTDOWN_NAME, errp);
335     execute_async(do_suspend, mode, errp);
336
337     if (error_is_set(errp)) {
338         g_free(mode);
339     }
340 }
341
342 void qmp_guest_suspend_hybrid(Error **errp)
343 {
344     error_set(errp, QERR_UNSUPPORTED);
345 }
346
347 GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp)
348 {
349     error_set(errp, QERR_UNSUPPORTED);
350     return NULL;
351 }
352
353 int64_t qmp_guest_get_time(Error **errp)
354 {
355     SYSTEMTIME ts = {0};
356     int64_t time_ns;
357     FILETIME tf;
358
359     GetSystemTime(&ts);
360     if (ts.wYear < 1601 || ts.wYear > 30827) {
361         error_setg(errp, "Failed to get time");
362         return -1;
363     }
364
365     if (!SystemTimeToFileTime(&ts, &tf)) {
366         error_setg(errp, "Failed to convert system time: %d", (int)GetLastError());
367         return -1;
368     }
369
370     time_ns = ((((int64_t)tf.dwHighDateTime << 32) | tf.dwLowDateTime)
371                 - W32_FT_OFFSET) * 100;
372
373     return time_ns;
374 }
375
376 void qmp_guest_set_time(bool has_time, int64_t time_ns, Error **errp)
377 {
378     SYSTEMTIME ts;
379     FILETIME tf;
380     LONGLONG time;
381
382     if (has_time) {
383         /* Okay, user passed a time to set. Validate it. */
384         if (time_ns < 0 || time_ns / 100 > INT64_MAX - W32_FT_OFFSET) {
385             error_setg(errp, "Time %" PRId64 "is invalid", time_ns);
386             return;
387         }
388
389         time = time_ns / 100 + W32_FT_OFFSET;
390
391         tf.dwLowDateTime = (DWORD) time;
392         tf.dwHighDateTime = (DWORD) (time >> 32);
393
394         if (!FileTimeToSystemTime(&tf, &ts)) {
395             error_setg(errp, "Failed to convert system time %d",
396                        (int)GetLastError());
397             return;
398         }
399     } else {
400         /* Otherwise read the time from RTC which contains the correct value.
401          * Hopefully. */
402         GetSystemTime(&ts);
403         if (ts.wYear < 1601 || ts.wYear > 30827) {
404             error_setg(errp, "Failed to get time");
405             return;
406         }
407     }
408
409     acquire_privilege(SE_SYSTEMTIME_NAME, errp);
410     if (error_is_set(errp)) {
411         return;
412     }
413
414     if (!SetSystemTime(&ts)) {
415         error_setg(errp, "Failed to set time to guest: %d", (int)GetLastError());
416         return;
417     }
418 }
419
420 GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp)
421 {
422     error_set(errp, QERR_UNSUPPORTED);
423     return NULL;
424 }
425
426 int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp)
427 {
428     error_set(errp, QERR_UNSUPPORTED);
429     return -1;
430 }
431
432 /* register init/cleanup routines for stateful command groups */
433 void ga_command_state_init(GAState *s, GACommandState *cs)
434 {
435     if (vss_init(true)) {
436         ga_command_state_add(cs, NULL, guest_fsfreeze_cleanup);
437     }
438 }
This page took 0.050295 seconds and 4 git commands to generate.