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 typedef enum { 177 GUEST_SUSPEND_MODE_DISK, 178 GUEST_SUSPEND_MODE_RAM 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 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"); 201 } 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: 210 error_set(&local_err, QERR_INVALID_PARAMETER_VALUE, "mode", 211 "GuestSuspendMode"); 212 } 213 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 233 void qmp_guest_suspend_disk(Error **err) 234 { 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 } 245 } 246 247 void qmp_guest_suspend_ram(Error **err) 248 { 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 } 259 } 260 261 void qmp_guest_suspend_hybrid(Error **err) 262 { 263 error_set(err, QERR_UNSUPPORTED); 264 } 265 266 GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **err) 267 { 268 error_set(err, QERR_UNSUPPORTED); 269 return NULL; 270 } 271 272 /* register init/cleanup routines for stateful command groups */ 273 void ga_command_state_init(GAState *s, GACommandState *cs) 274 { 275 } 276