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 "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 return; 185 } 186 187 typedef enum { 188 GUEST_SUSPEND_MODE_DISK, 189 GUEST_SUSPEND_MODE_RAM 190 } GuestSuspendMode; 191 192 static void check_suspend_mode(GuestSuspendMode mode, Error **err) 193 { 194 SYSTEM_POWER_CAPABILITIES sys_pwr_caps; 195 Error *local_err = NULL; 196 197 if (error_is_set(err)) { 198 return; 199 } 200 ZeroMemory(&sys_pwr_caps, sizeof(sys_pwr_caps)); 201 if (!GetPwrCapabilities(&sys_pwr_caps)) { 202 error_set(&local_err, QERR_QGA_COMMAND_FAILED, 203 "failed to determine guest suspend capabilities"); 204 goto out; 205 } 206 207 switch (mode) { 208 case GUEST_SUSPEND_MODE_DISK: 209 if (!sys_pwr_caps.SystemS4) { 210 error_set(&local_err, QERR_QGA_COMMAND_FAILED, 211 "suspend-to-disk not supported by OS"); 212 } 213 break; 214 case GUEST_SUSPEND_MODE_RAM: 215 if (!sys_pwr_caps.SystemS3) { 216 error_set(&local_err, QERR_QGA_COMMAND_FAILED, 217 "suspend-to-ram not supported by OS"); 218 } 219 break; 220 default: 221 error_set(&local_err, QERR_INVALID_PARAMETER_VALUE, "mode", 222 "GuestSuspendMode"); 223 } 224 225 out: 226 if (local_err) { 227 error_propagate(err, local_err); 228 } 229 } 230 231 static DWORD WINAPI do_suspend(LPVOID opaque) 232 { 233 GuestSuspendMode *mode = opaque; 234 DWORD ret = 0; 235 236 if (!SetSuspendState(*mode == GUEST_SUSPEND_MODE_DISK, TRUE, TRUE)) { 237 slog("failed to suspend guest, %s", GetLastError()); 238 ret = -1; 239 } 240 g_free(mode); 241 return ret; 242 } 243 244 void qmp_guest_suspend_disk(Error **err) 245 { 246 GuestSuspendMode *mode = g_malloc(sizeof(GuestSuspendMode)); 247 248 *mode = GUEST_SUSPEND_MODE_DISK; 249 check_suspend_mode(*mode, err); 250 acquire_privilege(SE_SHUTDOWN_NAME, err); 251 execute_async(do_suspend, mode, err); 252 253 if (error_is_set(err)) { 254 g_free(mode); 255 } 256 } 257 258 void qmp_guest_suspend_ram(Error **err) 259 { 260 GuestSuspendMode *mode = g_malloc(sizeof(GuestSuspendMode)); 261 262 *mode = GUEST_SUSPEND_MODE_RAM; 263 check_suspend_mode(*mode, err); 264 acquire_privilege(SE_SHUTDOWN_NAME, err); 265 execute_async(do_suspend, mode, err); 266 267 if (error_is_set(err)) { 268 g_free(mode); 269 } 270 } 271 272 void qmp_guest_suspend_hybrid(Error **err) 273 { 274 error_set(err, QERR_UNSUPPORTED); 275 } 276 277 GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **err) 278 { 279 error_set(err, QERR_UNSUPPORTED); 280 return NULL; 281 } 282 283 /* register init/cleanup routines for stateful command groups */ 284 void ga_command_state_init(GAState *s, GACommandState *cs) 285 { 286 } 287