]>
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" | |
19 | #include "qerror.h" | |
20 | ||
546b60d0 MR |
21 | #ifndef SHTDN_REASON_FLAG_PLANNED |
22 | #define SHTDN_REASON_FLAG_PLANNED 0x80000000 | |
23 | #endif | |
24 | ||
aa59637e | 25 | static void acquire_privilege(const char *name, Error **err) |
d8ca685a | 26 | { |
546b60d0 MR |
27 | HANDLE token; |
28 | TOKEN_PRIVILEGES priv; | |
aa59637e GH |
29 | Error *local_err = NULL; |
30 | ||
31 | if (error_is_set(err)) { | |
32 | return; | |
33 | } | |
34 | ||
35 | if (OpenProcessToken(GetCurrentProcess(), | |
36 | TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, &token)) | |
37 | { | |
38 | if (!LookupPrivilegeValue(NULL, name, &priv.Privileges[0].Luid)) { | |
39 | error_set(&local_err, QERR_QGA_COMMAND_FAILED, | |
40 | "no luid for requested privilege"); | |
41 | goto out; | |
42 | } | |
43 | ||
44 | priv.PrivilegeCount = 1; | |
45 | priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; | |
46 | ||
47 | if (!AdjustTokenPrivileges(token, FALSE, &priv, 0, NULL, 0)) { | |
48 | error_set(&local_err, QERR_QGA_COMMAND_FAILED, | |
49 | "unable to acquire requested privilege"); | |
50 | goto out; | |
51 | } | |
52 | ||
53 | CloseHandle(token); | |
54 | } else { | |
55 | error_set(&local_err, QERR_QGA_COMMAND_FAILED, | |
56 | "failed to open privilege token"); | |
57 | } | |
58 | ||
59 | out: | |
60 | if (local_err) { | |
61 | error_propagate(err, local_err); | |
62 | } | |
63 | } | |
64 | ||
65 | static void execute_async(DWORD WINAPI (*func)(LPVOID), LPVOID opaque, Error **err) | |
66 | { | |
67 | Error *local_err = NULL; | |
68 | ||
69 | if (error_is_set(err)) { | |
70 | return; | |
71 | } | |
72 | HANDLE thread = CreateThread(NULL, 0, func, opaque, 0, NULL); | |
73 | if (!thread) { | |
74 | error_set(&local_err, QERR_QGA_COMMAND_FAILED, | |
75 | "failed to dispatch asynchronous command"); | |
76 | error_propagate(err, local_err); | |
77 | } | |
78 | } | |
79 | ||
80 | void qmp_guest_shutdown(bool has_mode, const char *mode, Error **err) | |
81 | { | |
546b60d0 MR |
82 | UINT shutdown_flag = EWX_FORCE; |
83 | ||
84 | slog("guest-shutdown called, mode: %s", mode); | |
85 | ||
86 | if (!has_mode || strcmp(mode, "powerdown") == 0) { | |
87 | shutdown_flag |= EWX_POWEROFF; | |
88 | } else if (strcmp(mode, "halt") == 0) { | |
89 | shutdown_flag |= EWX_SHUTDOWN; | |
90 | } else if (strcmp(mode, "reboot") == 0) { | |
91 | shutdown_flag |= EWX_REBOOT; | |
92 | } else { | |
93 | error_set(err, QERR_INVALID_PARAMETER_VALUE, "mode", | |
94 | "halt|powerdown|reboot"); | |
95 | return; | |
96 | } | |
97 | ||
98 | /* Request a shutdown privilege, but try to shut down the system | |
99 | anyway. */ | |
aa59637e GH |
100 | acquire_privilege(SE_SHUTDOWN_NAME, err); |
101 | if (error_is_set(err)) { | |
102 | return; | |
546b60d0 MR |
103 | } |
104 | ||
105 | if (!ExitWindowsEx(shutdown_flag, SHTDN_REASON_FLAG_PLANNED)) { | |
106 | slog("guest-shutdown failed: %d", GetLastError()); | |
107 | error_set(err, QERR_UNDEFINED_ERROR); | |
108 | } | |
d8ca685a MR |
109 | } |
110 | ||
111 | int64_t qmp_guest_file_open(const char *path, bool has_mode, const char *mode, Error **err) | |
112 | { | |
113 | error_set(err, QERR_UNSUPPORTED); | |
114 | return 0; | |
115 | } | |
116 | ||
117 | void qmp_guest_file_close(int64_t handle, Error **err) | |
118 | { | |
119 | error_set(err, QERR_UNSUPPORTED); | |
120 | } | |
121 | ||
122 | GuestFileRead *qmp_guest_file_read(int64_t handle, bool has_count, | |
123 | int64_t count, Error **err) | |
124 | { | |
125 | error_set(err, QERR_UNSUPPORTED); | |
126 | return 0; | |
127 | } | |
128 | ||
129 | GuestFileWrite *qmp_guest_file_write(int64_t handle, const char *buf_b64, | |
130 | bool has_count, int64_t count, Error **err) | |
131 | { | |
132 | error_set(err, QERR_UNSUPPORTED); | |
133 | return 0; | |
134 | } | |
135 | ||
136 | GuestFileSeek *qmp_guest_file_seek(int64_t handle, int64_t offset, | |
137 | int64_t whence, Error **err) | |
138 | { | |
139 | error_set(err, QERR_UNSUPPORTED); | |
140 | return 0; | |
141 | } | |
142 | ||
143 | void qmp_guest_file_flush(int64_t handle, Error **err) | |
144 | { | |
145 | error_set(err, QERR_UNSUPPORTED); | |
146 | } | |
147 | ||
148 | /* | |
149 | * Return status of freeze/thaw | |
150 | */ | |
151 | GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **err) | |
152 | { | |
153 | error_set(err, QERR_UNSUPPORTED); | |
154 | return 0; | |
155 | } | |
156 | ||
157 | /* | |
158 | * Walk list of mounted file systems in the guest, and freeze the ones which | |
159 | * are real local file systems. | |
160 | */ | |
161 | int64_t qmp_guest_fsfreeze_freeze(Error **err) | |
162 | { | |
163 | error_set(err, QERR_UNSUPPORTED); | |
164 | return 0; | |
165 | } | |
166 | ||
167 | /* | |
168 | * Walk list of frozen file systems in the guest, and thaw them. | |
169 | */ | |
170 | int64_t qmp_guest_fsfreeze_thaw(Error **err) | |
171 | { | |
172 | error_set(err, QERR_UNSUPPORTED); | |
173 | return 0; | |
174 | } | |
175 | ||
aa59637e | 176 | typedef enum { |
f54603b6 MR |
177 | GUEST_SUSPEND_MODE_DISK, |
178 | GUEST_SUSPEND_MODE_RAM | |
aa59637e GH |
179 | } GuestSuspendMode; |
180 | ||
181 | static void check_suspend_mode(GuestSuspendMode mode, Error **err) | |
182 | { | |
183 | SYSTEM_POWER_CAPABILITIES sys_pwr_caps; | |
184 | Error *local_err = NULL; | |
185 | ||
186 | if (error_is_set(err)) { | |
187 | return; | |
188 | } | |
189 | ZeroMemory(&sys_pwr_caps, sizeof(sys_pwr_caps)); | |
190 | if (!GetPwrCapabilities(&sys_pwr_caps)) { | |
191 | error_set(&local_err, QERR_QGA_COMMAND_FAILED, | |
192 | "failed to determine guest suspend capabilities"); | |
193 | goto out; | |
194 | } | |
195 | ||
f54603b6 MR |
196 | switch (mode) { |
197 | case GUEST_SUSPEND_MODE_DISK: | |
198 | if (!sys_pwr_caps.SystemS4) { | |
199 | error_set(&local_err, QERR_QGA_COMMAND_FAILED, | |
200 | "suspend-to-disk not supported by OS"); | |
aa59637e | 201 | } |
f54603b6 MR |
202 | break; |
203 | case GUEST_SUSPEND_MODE_RAM: | |
204 | if (!sys_pwr_caps.SystemS3) { | |
205 | error_set(&local_err, QERR_QGA_COMMAND_FAILED, | |
206 | "suspend-to-ram not supported by OS"); | |
207 | } | |
208 | break; | |
209 | default: | |
aa59637e GH |
210 | error_set(&local_err, QERR_INVALID_PARAMETER_VALUE, "mode", |
211 | "GuestSuspendMode"); | |
aa59637e GH |
212 | } |
213 | ||
aa59637e GH |
214 | out: |
215 | if (local_err) { | |
216 | error_propagate(err, local_err); | |
217 | } | |
218 | } | |
219 | ||
220 | static DWORD WINAPI do_suspend(LPVOID opaque) | |
221 | { | |
222 | GuestSuspendMode *mode = opaque; | |
223 | DWORD ret = 0; | |
224 | ||
225 | if (!SetSuspendState(*mode == GUEST_SUSPEND_MODE_DISK, TRUE, TRUE)) { | |
226 | slog("failed to suspend guest, %s", GetLastError()); | |
227 | ret = -1; | |
228 | } | |
229 | g_free(mode); | |
230 | return ret; | |
231 | } | |
232 | ||
11d0f125 LC |
233 | void qmp_guest_suspend_disk(Error **err) |
234 | { | |
aa59637e GH |
235 | GuestSuspendMode *mode = g_malloc(sizeof(GuestSuspendMode)); |
236 | ||
237 | *mode = GUEST_SUSPEND_MODE_DISK; | |
238 | check_suspend_mode(*mode, err); | |
239 | acquire_privilege(SE_SHUTDOWN_NAME, err); | |
240 | execute_async(do_suspend, mode, err); | |
241 | ||
242 | if (error_is_set(err)) { | |
243 | g_free(mode); | |
244 | } | |
11d0f125 LC |
245 | } |
246 | ||
fbf42210 LC |
247 | void qmp_guest_suspend_ram(Error **err) |
248 | { | |
f54603b6 MR |
249 | GuestSuspendMode *mode = g_malloc(sizeof(GuestSuspendMode)); |
250 | ||
251 | *mode = GUEST_SUSPEND_MODE_RAM; | |
252 | check_suspend_mode(*mode, err); | |
253 | acquire_privilege(SE_SHUTDOWN_NAME, err); | |
254 | execute_async(do_suspend, mode, err); | |
255 | ||
256 | if (error_is_set(err)) { | |
257 | g_free(mode); | |
258 | } | |
fbf42210 LC |
259 | } |
260 | ||
95f4f404 LC |
261 | void qmp_guest_suspend_hybrid(Error **err) |
262 | { | |
263 | error_set(err, QERR_UNSUPPORTED); | |
264 | } | |
265 | ||
3424fc9f MP |
266 | GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **err) |
267 | { | |
268 | error_set(err, QERR_UNSUPPORTED); | |
269 | return NULL; | |
270 | } | |
271 | ||
d8ca685a MR |
272 | /* register init/cleanup routines for stateful command groups */ |
273 | void ga_command_state_init(GAState *s, GACommandState *cs) | |
274 | { | |
275 | } |