1 /* 2 * QEMU Guest Agent POSIX-specific command implementations 3 * 4 * Copyright IBM Corp. 2011 5 * 6 * Authors: 7 * Michael Roth <mdroth@linux.vnet.ibm.com> 8 * 9 * This work is licensed under the terms of the GNU GPL, version 2 or later. 10 * See the COPYING file in the top-level directory. 11 */ 12 13 #include <glib.h> 14 15 #if defined(__linux__) 16 #include <mntent.h> 17 #include <linux/fs.h> 18 19 #if defined(__linux__) && defined(FIFREEZE) 20 #define CONFIG_FSFREEZE 21 #endif 22 #endif 23 24 #include <sys/types.h> 25 #include <sys/ioctl.h> 26 #include "qga/guest-agent-core.h" 27 #include "qga-qmp-commands.h" 28 #include "qerror.h" 29 #include "qemu-queue.h" 30 31 static GAState *ga_state; 32 33 void qmp_guest_shutdown(bool has_mode, const char *mode, Error **err) 34 { 35 int ret; 36 const char *shutdown_flag; 37 38 slog("guest-shutdown called, mode: %s", mode); 39 if (!has_mode || strcmp(mode, "powerdown") == 0) { 40 shutdown_flag = "-P"; 41 } else if (strcmp(mode, "halt") == 0) { 42 shutdown_flag = "-H"; 43 } else if (strcmp(mode, "reboot") == 0) { 44 shutdown_flag = "-r"; 45 } else { 46 error_set(err, QERR_INVALID_PARAMETER_VALUE, "mode", 47 "halt|powerdown|reboot"); 48 return; 49 } 50 51 ret = fork(); 52 if (ret == 0) { 53 /* child, start the shutdown */ 54 setsid(); 55 fclose(stdin); 56 fclose(stdout); 57 fclose(stderr); 58 59 ret = execl("/sbin/shutdown", "shutdown", shutdown_flag, "+0", 60 "hypervisor initiated shutdown", (char*)NULL); 61 if (ret) { 62 slog("guest-shutdown failed: %s", strerror(errno)); 63 } 64 exit(!!ret); 65 } else if (ret < 0) { 66 error_set(err, QERR_UNDEFINED_ERROR); 67 } 68 } 69 70 typedef struct GuestFileHandle { 71 uint64_t id; 72 FILE *fh; 73 QTAILQ_ENTRY(GuestFileHandle) next; 74 } GuestFileHandle; 75 76 static struct { 77 QTAILQ_HEAD(, GuestFileHandle) filehandles; 78 } guest_file_state; 79 80 static void guest_file_handle_add(FILE *fh) 81 { 82 GuestFileHandle *gfh; 83 84 gfh = g_malloc0(sizeof(GuestFileHandle)); 85 gfh->id = fileno(fh); 86 gfh->fh = fh; 87 QTAILQ_INSERT_TAIL(&guest_file_state.filehandles, gfh, next); 88 } 89 90 static GuestFileHandle *guest_file_handle_find(int64_t id) 91 { 92 GuestFileHandle *gfh; 93 94 QTAILQ_FOREACH(gfh, &guest_file_state.filehandles, next) 95 { 96 if (gfh->id == id) { 97 return gfh; 98 } 99 } 100 101 return NULL; 102 } 103 104 int64_t qmp_guest_file_open(const char *path, bool has_mode, const char *mode, Error **err) 105 { 106 FILE *fh; 107 int fd; 108 int64_t ret = -1; 109 110 if (!has_mode) { 111 mode = "r"; 112 } 113 slog("guest-file-open called, filepath: %s, mode: %s", path, mode); 114 fh = fopen(path, mode); 115 if (!fh) { 116 error_set(err, QERR_OPEN_FILE_FAILED, path); 117 return -1; 118 } 119 120 /* set fd non-blocking to avoid common use cases (like reading from a 121 * named pipe) from hanging the agent 122 */ 123 fd = fileno(fh); 124 ret = fcntl(fd, F_GETFL); 125 ret = fcntl(fd, F_SETFL, ret | O_NONBLOCK); 126 if (ret == -1) { 127 error_set(err, QERR_QGA_COMMAND_FAILED, "fcntl() failed"); 128 fclose(fh); 129 return -1; 130 } 131 132 guest_file_handle_add(fh); 133 slog("guest-file-open, handle: %d", fd); 134 return fd; 135 } 136 137 void qmp_guest_file_close(int64_t handle, Error **err) 138 { 139 GuestFileHandle *gfh = guest_file_handle_find(handle); 140 int ret; 141 142 slog("guest-file-close called, handle: %ld", handle); 143 if (!gfh) { 144 error_set(err, QERR_FD_NOT_FOUND, "handle"); 145 return; 146 } 147 148 ret = fclose(gfh->fh); 149 if (ret == -1) { 150 error_set(err, QERR_QGA_COMMAND_FAILED, "fclose() failed"); 151 return; 152 } 153 154 QTAILQ_REMOVE(&guest_file_state.filehandles, gfh, next); 155 g_free(gfh); 156 } 157 158 struct GuestFileRead *qmp_guest_file_read(int64_t handle, bool has_count, 159 int64_t count, Error **err) 160 { 161 GuestFileHandle *gfh = guest_file_handle_find(handle); 162 GuestFileRead *read_data = NULL; 163 guchar *buf; 164 FILE *fh; 165 size_t read_count; 166 167 if (!gfh) { 168 error_set(err, QERR_FD_NOT_FOUND, "handle"); 169 return NULL; 170 } 171 172 if (!has_count) { 173 count = QGA_READ_COUNT_DEFAULT; 174 } else if (count < 0) { 175 error_set(err, QERR_INVALID_PARAMETER, "count"); 176 return NULL; 177 } 178 179 fh = gfh->fh; 180 buf = g_malloc0(count+1); 181 read_count = fread(buf, 1, count, fh); 182 if (ferror(fh)) { 183 slog("guest-file-read failed, handle: %ld", handle); 184 error_set(err, QERR_QGA_COMMAND_FAILED, "fread() failed"); 185 } else { 186 buf[read_count] = 0; 187 read_data = g_malloc0(sizeof(GuestFileRead)); 188 read_data->count = read_count; 189 read_data->eof = feof(fh); 190 if (read_count) { 191 read_data->buf_b64 = g_base64_encode(buf, read_count); 192 } 193 } 194 g_free(buf); 195 clearerr(fh); 196 197 return read_data; 198 } 199 200 GuestFileWrite *qmp_guest_file_write(int64_t handle, const char *buf_b64, 201 bool has_count, int64_t count, Error **err) 202 { 203 GuestFileWrite *write_data = NULL; 204 guchar *buf; 205 gsize buf_len; 206 int write_count; 207 GuestFileHandle *gfh = guest_file_handle_find(handle); 208 FILE *fh; 209 210 if (!gfh) { 211 error_set(err, QERR_FD_NOT_FOUND, "handle"); 212 return NULL; 213 } 214 215 fh = gfh->fh; 216 buf = g_base64_decode(buf_b64, &buf_len); 217 218 if (!has_count) { 219 count = buf_len; 220 } else if (count < 0 || count > buf_len) { 221 g_free(buf); 222 error_set(err, QERR_INVALID_PARAMETER, "count"); 223 return NULL; 224 } 225 226 write_count = fwrite(buf, 1, count, fh); 227 if (ferror(fh)) { 228 slog("guest-file-write failed, handle: %ld", handle); 229 error_set(err, QERR_QGA_COMMAND_FAILED, "fwrite() error"); 230 } else { 231 write_data = g_malloc0(sizeof(GuestFileWrite)); 232 write_data->count = write_count; 233 write_data->eof = feof(fh); 234 } 235 g_free(buf); 236 clearerr(fh); 237 238 return write_data; 239 } 240 241 struct GuestFileSeek *qmp_guest_file_seek(int64_t handle, int64_t offset, 242 int64_t whence, Error **err) 243 { 244 GuestFileHandle *gfh = guest_file_handle_find(handle); 245 GuestFileSeek *seek_data = NULL; 246 FILE *fh; 247 int ret; 248 249 if (!gfh) { 250 error_set(err, QERR_FD_NOT_FOUND, "handle"); 251 return NULL; 252 } 253 254 fh = gfh->fh; 255 ret = fseek(fh, offset, whence); 256 if (ret == -1) { 257 error_set(err, QERR_QGA_COMMAND_FAILED, strerror(errno)); 258 } else { 259 seek_data = g_malloc0(sizeof(GuestFileRead)); 260 seek_data->position = ftell(fh); 261 seek_data->eof = feof(fh); 262 } 263 clearerr(fh); 264 265 return seek_data; 266 } 267 268 void qmp_guest_file_flush(int64_t handle, Error **err) 269 { 270 GuestFileHandle *gfh = guest_file_handle_find(handle); 271 FILE *fh; 272 int ret; 273 274 if (!gfh) { 275 error_set(err, QERR_FD_NOT_FOUND, "handle"); 276 return; 277 } 278 279 fh = gfh->fh; 280 ret = fflush(fh); 281 if (ret == EOF) { 282 error_set(err, QERR_QGA_COMMAND_FAILED, strerror(errno)); 283 } 284 } 285 286 static void guest_file_init(void) 287 { 288 QTAILQ_INIT(&guest_file_state.filehandles); 289 } 290 291 #if defined(CONFIG_FSFREEZE) 292 static void disable_logging(void) 293 { 294 ga_disable_logging(ga_state); 295 } 296 297 static void enable_logging(void) 298 { 299 ga_enable_logging(ga_state); 300 } 301 302 typedef struct GuestFsfreezeMount { 303 char *dirname; 304 char *devtype; 305 QTAILQ_ENTRY(GuestFsfreezeMount) next; 306 } GuestFsfreezeMount; 307 308 struct { 309 GuestFsfreezeStatus status; 310 QTAILQ_HEAD(, GuestFsfreezeMount) mount_list; 311 } guest_fsfreeze_state; 312 313 /* 314 * Walk the mount table and build a list of local file systems 315 */ 316 static int guest_fsfreeze_build_mount_list(void) 317 { 318 struct mntent *ment; 319 GuestFsfreezeMount *mount, *temp; 320 char const *mtab = MOUNTED; 321 FILE *fp; 322 323 QTAILQ_FOREACH_SAFE(mount, &guest_fsfreeze_state.mount_list, next, temp) { 324 QTAILQ_REMOVE(&guest_fsfreeze_state.mount_list, mount, next); 325 g_free(mount->dirname); 326 g_free(mount->devtype); 327 g_free(mount); 328 } 329 330 fp = setmntent(mtab, "r"); 331 if (!fp) { 332 g_warning("fsfreeze: unable to read mtab"); 333 return -1; 334 } 335 336 while ((ment = getmntent(fp))) { 337 /* 338 * An entry which device name doesn't start with a '/' is 339 * either a dummy file system or a network file system. 340 * Add special handling for smbfs and cifs as is done by 341 * coreutils as well. 342 */ 343 if ((ment->mnt_fsname[0] != '/') || 344 (strcmp(ment->mnt_type, "smbfs") == 0) || 345 (strcmp(ment->mnt_type, "cifs") == 0)) { 346 continue; 347 } 348 349 mount = g_malloc0(sizeof(GuestFsfreezeMount)); 350 mount->dirname = g_strdup(ment->mnt_dir); 351 mount->devtype = g_strdup(ment->mnt_type); 352 353 QTAILQ_INSERT_TAIL(&guest_fsfreeze_state.mount_list, mount, next); 354 } 355 356 endmntent(fp); 357 358 return 0; 359 } 360 361 /* 362 * Return status of freeze/thaw 363 */ 364 GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **err) 365 { 366 return guest_fsfreeze_state.status; 367 } 368 369 /* 370 * Walk list of mounted file systems in the guest, and freeze the ones which 371 * are real local file systems. 372 */ 373 int64_t qmp_guest_fsfreeze_freeze(Error **err) 374 { 375 int ret = 0, i = 0; 376 struct GuestFsfreezeMount *mount, *temp; 377 int fd; 378 char err_msg[512]; 379 380 slog("guest-fsfreeze called"); 381 382 if (guest_fsfreeze_state.status == GUEST_FSFREEZE_STATUS_FROZEN) { 383 return 0; 384 } 385 386 ret = guest_fsfreeze_build_mount_list(); 387 if (ret < 0) { 388 return ret; 389 } 390 391 /* cannot risk guest agent blocking itself on a write in this state */ 392 disable_logging(); 393 394 QTAILQ_FOREACH_SAFE(mount, &guest_fsfreeze_state.mount_list, next, temp) { 395 fd = qemu_open(mount->dirname, O_RDONLY); 396 if (fd == -1) { 397 sprintf(err_msg, "failed to open %s, %s", mount->dirname, strerror(errno)); 398 error_set(err, QERR_QGA_COMMAND_FAILED, err_msg); 399 goto error; 400 } 401 402 /* we try to cull filesytems we know won't work in advance, but other 403 * filesytems may not implement fsfreeze for less obvious reasons. 404 * these will report EOPNOTSUPP, so we simply ignore them. when 405 * thawing, these filesystems will return an EINVAL instead, due to 406 * not being in a frozen state. Other filesystem-specific 407 * errors may result in EINVAL, however, so the user should check the 408 * number * of filesystems returned here against those returned by the 409 * thaw operation to determine whether everything completed 410 * successfully 411 */ 412 ret = ioctl(fd, FIFREEZE); 413 if (ret < 0 && errno != EOPNOTSUPP) { 414 sprintf(err_msg, "failed to freeze %s, %s", mount->dirname, strerror(errno)); 415 error_set(err, QERR_QGA_COMMAND_FAILED, err_msg); 416 close(fd); 417 goto error; 418 } 419 close(fd); 420 421 i++; 422 } 423 424 guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_FROZEN; 425 return i; 426 427 error: 428 if (i > 0) { 429 qmp_guest_fsfreeze_thaw(NULL); 430 } 431 return 0; 432 } 433 434 /* 435 * Walk list of frozen file systems in the guest, and thaw them. 436 */ 437 int64_t qmp_guest_fsfreeze_thaw(Error **err) 438 { 439 int ret; 440 GuestFsfreezeMount *mount, *temp; 441 int fd, i = 0; 442 bool has_error = false; 443 444 QTAILQ_FOREACH_SAFE(mount, &guest_fsfreeze_state.mount_list, next, temp) { 445 fd = qemu_open(mount->dirname, O_RDONLY); 446 if (fd == -1) { 447 has_error = true; 448 continue; 449 } 450 ret = ioctl(fd, FITHAW); 451 if (ret < 0 && errno != EOPNOTSUPP && errno != EINVAL) { 452 has_error = true; 453 close(fd); 454 continue; 455 } 456 close(fd); 457 i++; 458 } 459 460 if (has_error) { 461 guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_ERROR; 462 } else { 463 guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_THAWED; 464 } 465 enable_logging(); 466 return i; 467 } 468 469 static void guest_fsfreeze_init(void) 470 { 471 guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_THAWED; 472 QTAILQ_INIT(&guest_fsfreeze_state.mount_list); 473 } 474 475 static void guest_fsfreeze_cleanup(void) 476 { 477 int64_t ret; 478 Error *err = NULL; 479 480 if (guest_fsfreeze_state.status == GUEST_FSFREEZE_STATUS_FROZEN) { 481 ret = qmp_guest_fsfreeze_thaw(&err); 482 if (ret < 0 || err) { 483 slog("failed to clean up frozen filesystems"); 484 } 485 } 486 } 487 #else 488 /* 489 * Return status of freeze/thaw 490 */ 491 GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **err) 492 { 493 error_set(err, QERR_UNSUPPORTED); 494 495 return 0; 496 } 497 498 /* 499 * Walk list of mounted file systems in the guest, and freeze the ones which 500 * are real local file systems. 501 */ 502 int64_t qmp_guest_fsfreeze_freeze(Error **err) 503 { 504 error_set(err, QERR_UNSUPPORTED); 505 506 return 0; 507 } 508 509 /* 510 * Walk list of frozen file systems in the guest, and thaw them. 511 */ 512 int64_t qmp_guest_fsfreeze_thaw(Error **err) 513 { 514 error_set(err, QERR_UNSUPPORTED); 515 516 return 0; 517 } 518 #endif 519 520 /* register init/cleanup routines for stateful command groups */ 521 void ga_command_state_init(GAState *s, GACommandState *cs) 522 { 523 ga_state = s; 524 #if defined(CONFIG_FSFREEZE) 525 ga_command_state_add(cs, guest_fsfreeze_init, guest_fsfreeze_cleanup); 526 #endif 527 ga_command_state_add(cs, guest_file_init, NULL); 528 } 529