xref: /openbmc/qemu/qga/commands-win32.c (revision e1fe50dc)
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>
8  *  Gal Hammer        <ghammer@redhat.com>
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-qmp-commands.h"
19 #include "qapi/qmp/qerror.h"
20 
21 #ifndef SHTDN_REASON_FLAG_PLANNED
22 #define SHTDN_REASON_FLAG_PLANNED 0x80000000
23 #endif
24 
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 
31 static void acquire_privilege(const char *name, Error **err)
32 {
33     HANDLE token;
34     TOKEN_PRIVILEGES priv;
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 {
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. */
106     acquire_privilege(SE_SHUTDOWN_NAME, err);
107     if (error_is_set(err)) {
108         return;
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     }
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 
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);
189 }
190 
191 typedef enum {
192     GUEST_SUSPEND_MODE_DISK,
193     GUEST_SUSPEND_MODE_RAM
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 
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");
216         }
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:
225         error_set(&local_err, QERR_INVALID_PARAMETER_VALUE, "mode",
226                   "GuestSuspendMode");
227     }
228 
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 
248 void qmp_guest_suspend_disk(Error **err)
249 {
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     }
260 }
261 
262 void qmp_guest_suspend_ram(Error **err)
263 {
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     }
274 }
275 
276 void qmp_guest_suspend_hybrid(Error **err)
277 {
278     error_set(err, QERR_UNSUPPORTED);
279 }
280 
281 GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **err)
282 {
283     error_set(err, QERR_UNSUPPORTED);
284     return NULL;
285 }
286 
287 int64_t qmp_guest_get_time(Error **errp)
288 {
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;
308 }
309 
310 void qmp_guest_set_time(int64_t time_ns, Error **errp)
311 {
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     }
340 }
341 
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 
354 /* register init/cleanup routines for stateful command groups */
355 void ga_command_state_init(GAState *s, GACommandState *cs)
356 {
357 }
358