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