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 static void acquire_privilege(const char *name, Error **err) 26 { 27 HANDLE token; 28 TOKEN_PRIVILEGES priv; 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 { 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. */ 100 acquire_privilege(SE_SHUTDOWN_NAME, err); 101 if (error_is_set(err)) { 102 return; 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 } 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 176 /* 177 * Walk list of mounted file systems in the guest, and discard unused 178 * areas. 179 */ 180 void qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **err) 181 { 182 error_set(err, QERR_UNSUPPORTED); 183 } 184 185 typedef enum { 186 GUEST_SUSPEND_MODE_DISK, 187 GUEST_SUSPEND_MODE_RAM 188 } GuestSuspendMode; 189 190 static void check_suspend_mode(GuestSuspendMode mode, Error **err) 191 { 192 SYSTEM_POWER_CAPABILITIES sys_pwr_caps; 193 Error *local_err = NULL; 194 195 if (error_is_set(err)) { 196 return; 197 } 198 ZeroMemory(&sys_pwr_caps, sizeof(sys_pwr_caps)); 199 if (!GetPwrCapabilities(&sys_pwr_caps)) { 200 error_set(&local_err, QERR_QGA_COMMAND_FAILED, 201 "failed to determine guest suspend capabilities"); 202 goto out; 203 } 204 205 switch (mode) { 206 case GUEST_SUSPEND_MODE_DISK: 207 if (!sys_pwr_caps.SystemS4) { 208 error_set(&local_err, QERR_QGA_COMMAND_FAILED, 209 "suspend-to-disk not supported by OS"); 210 } 211 break; 212 case GUEST_SUSPEND_MODE_RAM: 213 if (!sys_pwr_caps.SystemS3) { 214 error_set(&local_err, QERR_QGA_COMMAND_FAILED, 215 "suspend-to-ram not supported by OS"); 216 } 217 break; 218 default: 219 error_set(&local_err, QERR_INVALID_PARAMETER_VALUE, "mode", 220 "GuestSuspendMode"); 221 } 222 223 out: 224 if (local_err) { 225 error_propagate(err, local_err); 226 } 227 } 228 229 static DWORD WINAPI do_suspend(LPVOID opaque) 230 { 231 GuestSuspendMode *mode = opaque; 232 DWORD ret = 0; 233 234 if (!SetSuspendState(*mode == GUEST_SUSPEND_MODE_DISK, TRUE, TRUE)) { 235 slog("failed to suspend guest, %s", GetLastError()); 236 ret = -1; 237 } 238 g_free(mode); 239 return ret; 240 } 241 242 void qmp_guest_suspend_disk(Error **err) 243 { 244 GuestSuspendMode *mode = g_malloc(sizeof(GuestSuspendMode)); 245 246 *mode = GUEST_SUSPEND_MODE_DISK; 247 check_suspend_mode(*mode, err); 248 acquire_privilege(SE_SHUTDOWN_NAME, err); 249 execute_async(do_suspend, mode, err); 250 251 if (error_is_set(err)) { 252 g_free(mode); 253 } 254 } 255 256 void qmp_guest_suspend_ram(Error **err) 257 { 258 GuestSuspendMode *mode = g_malloc(sizeof(GuestSuspendMode)); 259 260 *mode = GUEST_SUSPEND_MODE_RAM; 261 check_suspend_mode(*mode, err); 262 acquire_privilege(SE_SHUTDOWN_NAME, err); 263 execute_async(do_suspend, mode, err); 264 265 if (error_is_set(err)) { 266 g_free(mode); 267 } 268 } 269 270 void qmp_guest_suspend_hybrid(Error **err) 271 { 272 error_set(err, QERR_UNSUPPORTED); 273 } 274 275 GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **err) 276 { 277 error_set(err, QERR_UNSUPPORTED); 278 return NULL; 279 } 280 281 int64_t qmp_guest_get_time(Error **errp) 282 { 283 error_set(errp, QERR_UNSUPPORTED); 284 return -1; 285 } 286 287 void qmp_guest_set_time(int64_t time_ns, Error **errp) 288 { 289 error_set(errp, QERR_UNSUPPORTED); 290 } 291 292 GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp) 293 { 294 error_set(errp, QERR_UNSUPPORTED); 295 return NULL; 296 } 297 298 int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp) 299 { 300 error_set(errp, QERR_UNSUPPORTED); 301 return -1; 302 } 303 304 /* register init/cleanup routines for stateful command groups */ 305 void ga_command_state_init(GAState *s, GACommandState *cs) 306 { 307 } 308