|
|
1.1 ! root 1: /* ! 2: * QEMU Guest Agent POSIX-specific command implementations ! 3: * ! 4: * Copyright IBM Corp. 2011 ! 5: * ! 6: * Authors: ! 7: * Michael Roth <[email protected]> ! 8: * Michal Privoznik <[email protected]> ! 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 <sys/types.h> ! 16: #include <sys/ioctl.h> ! 17: #include <sys/wait.h> ! 18: #include "qga/guest-agent-core.h" ! 19: #include "qga-qmp-commands.h" ! 20: #include "qerror.h" ! 21: #include "qemu-queue.h" ! 22: #include "host-utils.h" ! 23: ! 24: #ifndef CONFIG_HAS_ENVIRON ! 25: #ifdef __APPLE__ ! 26: #include <crt_externs.h> ! 27: #define environ (*_NSGetEnviron()) ! 28: #else ! 29: extern char **environ; ! 30: #endif ! 31: #endif ! 32: ! 33: #if defined(__linux__) ! 34: #include <mntent.h> ! 35: #include <linux/fs.h> ! 36: #include <ifaddrs.h> ! 37: #include <arpa/inet.h> ! 38: #include <sys/socket.h> ! 39: #include <net/if.h> ! 40: ! 41: #if defined(__linux__) && defined(FIFREEZE) ! 42: #define CONFIG_FSFREEZE ! 43: #endif ! 44: #endif ! 45: ! 46: void qmp_guest_shutdown(bool has_mode, const char *mode, Error **err) ! 47: { ! 48: const char *shutdown_flag; ! 49: pid_t rpid, pid; ! 50: int status; ! 51: ! 52: slog("guest-shutdown called, mode: %s", mode); ! 53: if (!has_mode || strcmp(mode, "powerdown") == 0) { ! 54: shutdown_flag = "-P"; ! 55: } else if (strcmp(mode, "halt") == 0) { ! 56: shutdown_flag = "-H"; ! 57: } else if (strcmp(mode, "reboot") == 0) { ! 58: shutdown_flag = "-r"; ! 59: } else { ! 60: error_set(err, QERR_INVALID_PARAMETER_VALUE, "mode", ! 61: "halt|powerdown|reboot"); ! 62: return; ! 63: } ! 64: ! 65: pid = fork(); ! 66: if (pid == 0) { ! 67: /* child, start the shutdown */ ! 68: setsid(); ! 69: reopen_fd_to_null(0); ! 70: reopen_fd_to_null(1); ! 71: reopen_fd_to_null(2); ! 72: ! 73: execle("/sbin/shutdown", "shutdown", shutdown_flag, "+0", ! 74: "hypervisor initiated shutdown", (char*)NULL, environ); ! 75: _exit(EXIT_FAILURE); ! 76: } else if (pid < 0) { ! 77: goto exit_err; ! 78: } ! 79: ! 80: do { ! 81: rpid = waitpid(pid, &status, 0); ! 82: } while (rpid == -1 && errno == EINTR); ! 83: if (rpid == pid && WIFEXITED(status) && !WEXITSTATUS(status)) { ! 84: return; ! 85: } ! 86: ! 87: exit_err: ! 88: error_set(err, QERR_UNDEFINED_ERROR); ! 89: } ! 90: ! 91: typedef struct GuestFileHandle { ! 92: uint64_t id; ! 93: FILE *fh; ! 94: QTAILQ_ENTRY(GuestFileHandle) next; ! 95: } GuestFileHandle; ! 96: ! 97: static struct { ! 98: QTAILQ_HEAD(, GuestFileHandle) filehandles; ! 99: } guest_file_state; ! 100: ! 101: static void guest_file_handle_add(FILE *fh) ! 102: { ! 103: GuestFileHandle *gfh; ! 104: ! 105: gfh = g_malloc0(sizeof(GuestFileHandle)); ! 106: gfh->id = fileno(fh); ! 107: gfh->fh = fh; ! 108: QTAILQ_INSERT_TAIL(&guest_file_state.filehandles, gfh, next); ! 109: } ! 110: ! 111: static GuestFileHandle *guest_file_handle_find(int64_t id) ! 112: { ! 113: GuestFileHandle *gfh; ! 114: ! 115: QTAILQ_FOREACH(gfh, &guest_file_state.filehandles, next) ! 116: { ! 117: if (gfh->id == id) { ! 118: return gfh; ! 119: } ! 120: } ! 121: ! 122: return NULL; ! 123: } ! 124: ! 125: int64_t qmp_guest_file_open(const char *path, bool has_mode, const char *mode, Error **err) ! 126: { ! 127: FILE *fh; ! 128: int fd; ! 129: int64_t ret = -1; ! 130: ! 131: if (!has_mode) { ! 132: mode = "r"; ! 133: } ! 134: slog("guest-file-open called, filepath: %s, mode: %s", path, mode); ! 135: fh = fopen(path, mode); ! 136: if (!fh) { ! 137: error_set(err, QERR_OPEN_FILE_FAILED, path); ! 138: return -1; ! 139: } ! 140: ! 141: /* set fd non-blocking to avoid common use cases (like reading from a ! 142: * named pipe) from hanging the agent ! 143: */ ! 144: fd = fileno(fh); ! 145: ret = fcntl(fd, F_GETFL); ! 146: ret = fcntl(fd, F_SETFL, ret | O_NONBLOCK); ! 147: if (ret == -1) { ! 148: error_set(err, QERR_QGA_COMMAND_FAILED, "fcntl() failed"); ! 149: fclose(fh); ! 150: return -1; ! 151: } ! 152: ! 153: guest_file_handle_add(fh); ! 154: slog("guest-file-open, handle: %d", fd); ! 155: return fd; ! 156: } ! 157: ! 158: void qmp_guest_file_close(int64_t handle, Error **err) ! 159: { ! 160: GuestFileHandle *gfh = guest_file_handle_find(handle); ! 161: int ret; ! 162: ! 163: slog("guest-file-close called, handle: %ld", handle); ! 164: if (!gfh) { ! 165: error_set(err, QERR_FD_NOT_FOUND, "handle"); ! 166: return; ! 167: } ! 168: ! 169: ret = fclose(gfh->fh); ! 170: if (ret == -1) { ! 171: error_set(err, QERR_QGA_COMMAND_FAILED, "fclose() failed"); ! 172: return; ! 173: } ! 174: ! 175: QTAILQ_REMOVE(&guest_file_state.filehandles, gfh, next); ! 176: g_free(gfh); ! 177: } ! 178: ! 179: struct GuestFileRead *qmp_guest_file_read(int64_t handle, bool has_count, ! 180: int64_t count, Error **err) ! 181: { ! 182: GuestFileHandle *gfh = guest_file_handle_find(handle); ! 183: GuestFileRead *read_data = NULL; ! 184: guchar *buf; ! 185: FILE *fh; ! 186: size_t read_count; ! 187: ! 188: if (!gfh) { ! 189: error_set(err, QERR_FD_NOT_FOUND, "handle"); ! 190: return NULL; ! 191: } ! 192: ! 193: if (!has_count) { ! 194: count = QGA_READ_COUNT_DEFAULT; ! 195: } else if (count < 0) { ! 196: error_set(err, QERR_INVALID_PARAMETER, "count"); ! 197: return NULL; ! 198: } ! 199: ! 200: fh = gfh->fh; ! 201: buf = g_malloc0(count+1); ! 202: read_count = fread(buf, 1, count, fh); ! 203: if (ferror(fh)) { ! 204: slog("guest-file-read failed, handle: %ld", handle); ! 205: error_set(err, QERR_QGA_COMMAND_FAILED, "fread() failed"); ! 206: } else { ! 207: buf[read_count] = 0; ! 208: read_data = g_malloc0(sizeof(GuestFileRead)); ! 209: read_data->count = read_count; ! 210: read_data->eof = feof(fh); ! 211: if (read_count) { ! 212: read_data->buf_b64 = g_base64_encode(buf, read_count); ! 213: } ! 214: } ! 215: g_free(buf); ! 216: clearerr(fh); ! 217: ! 218: return read_data; ! 219: } ! 220: ! 221: GuestFileWrite *qmp_guest_file_write(int64_t handle, const char *buf_b64, ! 222: bool has_count, int64_t count, Error **err) ! 223: { ! 224: GuestFileWrite *write_data = NULL; ! 225: guchar *buf; ! 226: gsize buf_len; ! 227: int write_count; ! 228: GuestFileHandle *gfh = guest_file_handle_find(handle); ! 229: FILE *fh; ! 230: ! 231: if (!gfh) { ! 232: error_set(err, QERR_FD_NOT_FOUND, "handle"); ! 233: return NULL; ! 234: } ! 235: ! 236: fh = gfh->fh; ! 237: buf = g_base64_decode(buf_b64, &buf_len); ! 238: ! 239: if (!has_count) { ! 240: count = buf_len; ! 241: } else if (count < 0 || count > buf_len) { ! 242: g_free(buf); ! 243: error_set(err, QERR_INVALID_PARAMETER, "count"); ! 244: return NULL; ! 245: } ! 246: ! 247: write_count = fwrite(buf, 1, count, fh); ! 248: if (ferror(fh)) { ! 249: slog("guest-file-write failed, handle: %ld", handle); ! 250: error_set(err, QERR_QGA_COMMAND_FAILED, "fwrite() error"); ! 251: } else { ! 252: write_data = g_malloc0(sizeof(GuestFileWrite)); ! 253: write_data->count = write_count; ! 254: write_data->eof = feof(fh); ! 255: } ! 256: g_free(buf); ! 257: clearerr(fh); ! 258: ! 259: return write_data; ! 260: } ! 261: ! 262: struct GuestFileSeek *qmp_guest_file_seek(int64_t handle, int64_t offset, ! 263: int64_t whence, Error **err) ! 264: { ! 265: GuestFileHandle *gfh = guest_file_handle_find(handle); ! 266: GuestFileSeek *seek_data = NULL; ! 267: FILE *fh; ! 268: int ret; ! 269: ! 270: if (!gfh) { ! 271: error_set(err, QERR_FD_NOT_FOUND, "handle"); ! 272: return NULL; ! 273: } ! 274: ! 275: fh = gfh->fh; ! 276: ret = fseek(fh, offset, whence); ! 277: if (ret == -1) { ! 278: error_set(err, QERR_QGA_COMMAND_FAILED, strerror(errno)); ! 279: } else { ! 280: seek_data = g_malloc0(sizeof(GuestFileRead)); ! 281: seek_data->position = ftell(fh); ! 282: seek_data->eof = feof(fh); ! 283: } ! 284: clearerr(fh); ! 285: ! 286: return seek_data; ! 287: } ! 288: ! 289: void qmp_guest_file_flush(int64_t handle, Error **err) ! 290: { ! 291: GuestFileHandle *gfh = guest_file_handle_find(handle); ! 292: FILE *fh; ! 293: int ret; ! 294: ! 295: if (!gfh) { ! 296: error_set(err, QERR_FD_NOT_FOUND, "handle"); ! 297: return; ! 298: } ! 299: ! 300: fh = gfh->fh; ! 301: ret = fflush(fh); ! 302: if (ret == EOF) { ! 303: error_set(err, QERR_QGA_COMMAND_FAILED, strerror(errno)); ! 304: } ! 305: } ! 306: ! 307: static void guest_file_init(void) ! 308: { ! 309: QTAILQ_INIT(&guest_file_state.filehandles); ! 310: } ! 311: ! 312: /* linux-specific implementations. avoid this if at all possible. */ ! 313: #if defined(__linux__) ! 314: ! 315: #if defined(CONFIG_FSFREEZE) ! 316: ! 317: typedef struct GuestFsfreezeMount { ! 318: char *dirname; ! 319: char *devtype; ! 320: QTAILQ_ENTRY(GuestFsfreezeMount) next; ! 321: } GuestFsfreezeMount; ! 322: ! 323: typedef QTAILQ_HEAD(, GuestFsfreezeMount) GuestFsfreezeMountList; ! 324: ! 325: static void guest_fsfreeze_free_mount_list(GuestFsfreezeMountList *mounts) ! 326: { ! 327: GuestFsfreezeMount *mount, *temp; ! 328: ! 329: if (!mounts) { ! 330: return; ! 331: } ! 332: ! 333: QTAILQ_FOREACH_SAFE(mount, mounts, next, temp) { ! 334: QTAILQ_REMOVE(mounts, mount, next); ! 335: g_free(mount->dirname); ! 336: g_free(mount->devtype); ! 337: g_free(mount); ! 338: } ! 339: } ! 340: ! 341: /* ! 342: * Walk the mount table and build a list of local file systems ! 343: */ ! 344: static int guest_fsfreeze_build_mount_list(GuestFsfreezeMountList *mounts) ! 345: { ! 346: struct mntent *ment; ! 347: GuestFsfreezeMount *mount; ! 348: char const *mtab = "/proc/self/mounts"; ! 349: FILE *fp; ! 350: ! 351: fp = setmntent(mtab, "r"); ! 352: if (!fp) { ! 353: g_warning("fsfreeze: unable to read mtab"); ! 354: return -1; ! 355: } ! 356: ! 357: while ((ment = getmntent(fp))) { ! 358: /* ! 359: * An entry which device name doesn't start with a '/' is ! 360: * either a dummy file system or a network file system. ! 361: * Add special handling for smbfs and cifs as is done by ! 362: * coreutils as well. ! 363: */ ! 364: if ((ment->mnt_fsname[0] != '/') || ! 365: (strcmp(ment->mnt_type, "smbfs") == 0) || ! 366: (strcmp(ment->mnt_type, "cifs") == 0)) { ! 367: continue; ! 368: } ! 369: ! 370: mount = g_malloc0(sizeof(GuestFsfreezeMount)); ! 371: mount->dirname = g_strdup(ment->mnt_dir); ! 372: mount->devtype = g_strdup(ment->mnt_type); ! 373: ! 374: QTAILQ_INSERT_TAIL(mounts, mount, next); ! 375: } ! 376: ! 377: endmntent(fp); ! 378: ! 379: return 0; ! 380: } ! 381: ! 382: /* ! 383: * Return status of freeze/thaw ! 384: */ ! 385: GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **err) ! 386: { ! 387: if (ga_is_frozen(ga_state)) { ! 388: return GUEST_FSFREEZE_STATUS_FROZEN; ! 389: } ! 390: ! 391: return GUEST_FSFREEZE_STATUS_THAWED; ! 392: } ! 393: ! 394: /* ! 395: * Walk list of mounted file systems in the guest, and freeze the ones which ! 396: * are real local file systems. ! 397: */ ! 398: int64_t qmp_guest_fsfreeze_freeze(Error **err) ! 399: { ! 400: int ret = 0, i = 0; ! 401: GuestFsfreezeMountList mounts; ! 402: struct GuestFsfreezeMount *mount; ! 403: int fd; ! 404: char err_msg[512]; ! 405: ! 406: slog("guest-fsfreeze called"); ! 407: ! 408: QTAILQ_INIT(&mounts); ! 409: ret = guest_fsfreeze_build_mount_list(&mounts); ! 410: if (ret < 0) { ! 411: return ret; ! 412: } ! 413: ! 414: /* cannot risk guest agent blocking itself on a write in this state */ ! 415: ga_set_frozen(ga_state); ! 416: ! 417: QTAILQ_FOREACH(mount, &mounts, next) { ! 418: fd = qemu_open(mount->dirname, O_RDONLY); ! 419: if (fd == -1) { ! 420: sprintf(err_msg, "failed to open %s, %s", mount->dirname, ! 421: strerror(errno)); ! 422: error_set(err, QERR_QGA_COMMAND_FAILED, err_msg); ! 423: goto error; ! 424: } ! 425: ! 426: /* we try to cull filesytems we know won't work in advance, but other ! 427: * filesytems may not implement fsfreeze for less obvious reasons. ! 428: * these will report EOPNOTSUPP. we simply ignore these when tallying ! 429: * the number of frozen filesystems. ! 430: * ! 431: * any other error means a failure to freeze a filesystem we ! 432: * expect to be freezable, so return an error in those cases ! 433: * and return system to thawed state. ! 434: */ ! 435: ret = ioctl(fd, FIFREEZE); ! 436: if (ret == -1) { ! 437: if (errno != EOPNOTSUPP) { ! 438: sprintf(err_msg, "failed to freeze %s, %s", ! 439: mount->dirname, strerror(errno)); ! 440: error_set(err, QERR_QGA_COMMAND_FAILED, err_msg); ! 441: close(fd); ! 442: goto error; ! 443: } ! 444: } else { ! 445: i++; ! 446: } ! 447: close(fd); ! 448: } ! 449: ! 450: guest_fsfreeze_free_mount_list(&mounts); ! 451: return i; ! 452: ! 453: error: ! 454: guest_fsfreeze_free_mount_list(&mounts); ! 455: qmp_guest_fsfreeze_thaw(NULL); ! 456: return 0; ! 457: } ! 458: ! 459: /* ! 460: * Walk list of frozen file systems in the guest, and thaw them. ! 461: */ ! 462: int64_t qmp_guest_fsfreeze_thaw(Error **err) ! 463: { ! 464: int ret; ! 465: GuestFsfreezeMountList mounts; ! 466: GuestFsfreezeMount *mount; ! 467: int fd, i = 0, logged; ! 468: ! 469: QTAILQ_INIT(&mounts); ! 470: ret = guest_fsfreeze_build_mount_list(&mounts); ! 471: if (ret) { ! 472: error_set(err, QERR_QGA_COMMAND_FAILED, ! 473: "failed to enumerate filesystems"); ! 474: return 0; ! 475: } ! 476: ! 477: QTAILQ_FOREACH(mount, &mounts, next) { ! 478: logged = false; ! 479: fd = qemu_open(mount->dirname, O_RDONLY); ! 480: if (fd == -1) { ! 481: continue; ! 482: } ! 483: /* we have no way of knowing whether a filesystem was actually unfrozen ! 484: * as a result of a successful call to FITHAW, only that if an error ! 485: * was returned the filesystem was *not* unfrozen by that particular ! 486: * call. ! 487: * ! 488: * since multiple preceding FIFREEZEs require multiple calls to FITHAW ! 489: * to unfreeze, continuing issuing FITHAW until an error is returned, ! 490: * in which case either the filesystem is in an unfreezable state, or, ! 491: * more likely, it was thawed previously (and remains so afterward). ! 492: * ! 493: * also, since the most recent successful call is the one that did ! 494: * the actual unfreeze, we can use this to provide an accurate count ! 495: * of the number of filesystems unfrozen by guest-fsfreeze-thaw, which ! 496: * may * be useful for determining whether a filesystem was unfrozen ! 497: * during the freeze/thaw phase by a process other than qemu-ga. ! 498: */ ! 499: do { ! 500: ret = ioctl(fd, FITHAW); ! 501: if (ret == 0 && !logged) { ! 502: i++; ! 503: logged = true; ! 504: } ! 505: } while (ret == 0); ! 506: close(fd); ! 507: } ! 508: ! 509: ga_unset_frozen(ga_state); ! 510: guest_fsfreeze_free_mount_list(&mounts); ! 511: return i; ! 512: } ! 513: ! 514: static void guest_fsfreeze_cleanup(void) ! 515: { ! 516: int64_t ret; ! 517: Error *err = NULL; ! 518: ! 519: if (ga_is_frozen(ga_state) == GUEST_FSFREEZE_STATUS_FROZEN) { ! 520: ret = qmp_guest_fsfreeze_thaw(&err); ! 521: if (ret < 0 || err) { ! 522: slog("failed to clean up frozen filesystems"); ! 523: } ! 524: } ! 525: } ! 526: #endif /* CONFIG_FSFREEZE */ ! 527: ! 528: #define LINUX_SYS_STATE_FILE "/sys/power/state" ! 529: #define SUSPEND_SUPPORTED 0 ! 530: #define SUSPEND_NOT_SUPPORTED 1 ! 531: ! 532: static void bios_supports_mode(const char *pmutils_bin, const char *pmutils_arg, ! 533: const char *sysfile_str, Error **err) ! 534: { ! 535: char *pmutils_path; ! 536: pid_t pid, rpid; ! 537: int status; ! 538: ! 539: pmutils_path = g_find_program_in_path(pmutils_bin); ! 540: ! 541: pid = fork(); ! 542: if (!pid) { ! 543: char buf[32]; /* hopefully big enough */ ! 544: ssize_t ret; ! 545: int fd; ! 546: ! 547: setsid(); ! 548: reopen_fd_to_null(0); ! 549: reopen_fd_to_null(1); ! 550: reopen_fd_to_null(2); ! 551: ! 552: if (pmutils_path) { ! 553: execle(pmutils_path, pmutils_bin, pmutils_arg, NULL, environ); ! 554: } ! 555: ! 556: /* ! 557: * If we get here either pm-utils is not installed or execle() has ! 558: * failed. Let's try the manual method if the caller wants it. ! 559: */ ! 560: ! 561: if (!sysfile_str) { ! 562: _exit(SUSPEND_NOT_SUPPORTED); ! 563: } ! 564: ! 565: fd = open(LINUX_SYS_STATE_FILE, O_RDONLY); ! 566: if (fd < 0) { ! 567: _exit(SUSPEND_NOT_SUPPORTED); ! 568: } ! 569: ! 570: ret = read(fd, buf, sizeof(buf)-1); ! 571: if (ret <= 0) { ! 572: _exit(SUSPEND_NOT_SUPPORTED); ! 573: } ! 574: buf[ret] = '\0'; ! 575: ! 576: if (strstr(buf, sysfile_str)) { ! 577: _exit(SUSPEND_SUPPORTED); ! 578: } ! 579: ! 580: _exit(SUSPEND_NOT_SUPPORTED); ! 581: } ! 582: ! 583: g_free(pmutils_path); ! 584: ! 585: if (pid < 0) { ! 586: goto undef_err; ! 587: } ! 588: ! 589: do { ! 590: rpid = waitpid(pid, &status, 0); ! 591: } while (rpid == -1 && errno == EINTR); ! 592: if (rpid == pid && WIFEXITED(status)) { ! 593: switch (WEXITSTATUS(status)) { ! 594: case SUSPEND_SUPPORTED: ! 595: return; ! 596: case SUSPEND_NOT_SUPPORTED: ! 597: error_set(err, QERR_UNSUPPORTED); ! 598: return; ! 599: default: ! 600: goto undef_err; ! 601: } ! 602: } ! 603: ! 604: undef_err: ! 605: error_set(err, QERR_UNDEFINED_ERROR); ! 606: } ! 607: ! 608: static void guest_suspend(const char *pmutils_bin, const char *sysfile_str, ! 609: Error **err) ! 610: { ! 611: char *pmutils_path; ! 612: pid_t rpid, pid; ! 613: int status; ! 614: ! 615: pmutils_path = g_find_program_in_path(pmutils_bin); ! 616: ! 617: pid = fork(); ! 618: if (pid == 0) { ! 619: /* child */ ! 620: int fd; ! 621: ! 622: setsid(); ! 623: reopen_fd_to_null(0); ! 624: reopen_fd_to_null(1); ! 625: reopen_fd_to_null(2); ! 626: ! 627: if (pmutils_path) { ! 628: execle(pmutils_path, pmutils_bin, NULL, environ); ! 629: } ! 630: ! 631: /* ! 632: * If we get here either pm-utils is not installed or execle() has ! 633: * failed. Let's try the manual method if the caller wants it. ! 634: */ ! 635: ! 636: if (!sysfile_str) { ! 637: _exit(EXIT_FAILURE); ! 638: } ! 639: ! 640: fd = open(LINUX_SYS_STATE_FILE, O_WRONLY); ! 641: if (fd < 0) { ! 642: _exit(EXIT_FAILURE); ! 643: } ! 644: ! 645: if (write(fd, sysfile_str, strlen(sysfile_str)) < 0) { ! 646: _exit(EXIT_FAILURE); ! 647: } ! 648: ! 649: _exit(EXIT_SUCCESS); ! 650: } ! 651: ! 652: g_free(pmutils_path); ! 653: ! 654: if (pid < 0) { ! 655: goto exit_err; ! 656: } ! 657: ! 658: do { ! 659: rpid = waitpid(pid, &status, 0); ! 660: } while (rpid == -1 && errno == EINTR); ! 661: if (rpid == pid && WIFEXITED(status) && !WEXITSTATUS(status)) { ! 662: return; ! 663: } ! 664: ! 665: exit_err: ! 666: error_set(err, QERR_UNDEFINED_ERROR); ! 667: } ! 668: ! 669: void qmp_guest_suspend_disk(Error **err) ! 670: { ! 671: bios_supports_mode("pm-is-supported", "--hibernate", "disk", err); ! 672: if (error_is_set(err)) { ! 673: return; ! 674: } ! 675: ! 676: guest_suspend("pm-hibernate", "disk", err); ! 677: } ! 678: ! 679: void qmp_guest_suspend_ram(Error **err) ! 680: { ! 681: bios_supports_mode("pm-is-supported", "--suspend", "mem", err); ! 682: if (error_is_set(err)) { ! 683: return; ! 684: } ! 685: ! 686: guest_suspend("pm-suspend", "mem", err); ! 687: } ! 688: ! 689: void qmp_guest_suspend_hybrid(Error **err) ! 690: { ! 691: bios_supports_mode("pm-is-supported", "--suspend-hybrid", NULL, err); ! 692: if (error_is_set(err)) { ! 693: return; ! 694: } ! 695: ! 696: guest_suspend("pm-suspend-hybrid", NULL, err); ! 697: } ! 698: ! 699: static GuestNetworkInterfaceList * ! 700: guest_find_interface(GuestNetworkInterfaceList *head, ! 701: const char *name) ! 702: { ! 703: for (; head; head = head->next) { ! 704: if (strcmp(head->value->name, name) == 0) { ! 705: break; ! 706: } ! 707: } ! 708: ! 709: return head; ! 710: } ! 711: ! 712: /* ! 713: * Build information about guest interfaces ! 714: */ ! 715: GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp) ! 716: { ! 717: GuestNetworkInterfaceList *head = NULL, *cur_item = NULL; ! 718: struct ifaddrs *ifap, *ifa; ! 719: char err_msg[512]; ! 720: ! 721: if (getifaddrs(&ifap) < 0) { ! 722: snprintf(err_msg, sizeof(err_msg), ! 723: "getifaddrs failed: %s", strerror(errno)); ! 724: error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg); ! 725: goto error; ! 726: } ! 727: ! 728: for (ifa = ifap; ifa; ifa = ifa->ifa_next) { ! 729: GuestNetworkInterfaceList *info; ! 730: GuestIpAddressList **address_list = NULL, *address_item = NULL; ! 731: char addr4[INET_ADDRSTRLEN]; ! 732: char addr6[INET6_ADDRSTRLEN]; ! 733: int sock; ! 734: struct ifreq ifr; ! 735: unsigned char *mac_addr; ! 736: void *p; ! 737: ! 738: g_debug("Processing %s interface", ifa->ifa_name); ! 739: ! 740: info = guest_find_interface(head, ifa->ifa_name); ! 741: ! 742: if (!info) { ! 743: info = g_malloc0(sizeof(*info)); ! 744: info->value = g_malloc0(sizeof(*info->value)); ! 745: info->value->name = g_strdup(ifa->ifa_name); ! 746: ! 747: if (!cur_item) { ! 748: head = cur_item = info; ! 749: } else { ! 750: cur_item->next = info; ! 751: cur_item = info; ! 752: } ! 753: } ! 754: ! 755: if (!info->value->has_hardware_address && ! 756: ifa->ifa_flags & SIOCGIFHWADDR) { ! 757: /* we haven't obtained HW address yet */ ! 758: sock = socket(PF_INET, SOCK_STREAM, 0); ! 759: if (sock == -1) { ! 760: snprintf(err_msg, sizeof(err_msg), ! 761: "failed to create socket: %s", strerror(errno)); ! 762: error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg); ! 763: goto error; ! 764: } ! 765: ! 766: memset(&ifr, 0, sizeof(ifr)); ! 767: strncpy(ifr.ifr_name, info->value->name, IF_NAMESIZE); ! 768: if (ioctl(sock, SIOCGIFHWADDR, &ifr) == -1) { ! 769: snprintf(err_msg, sizeof(err_msg), ! 770: "failed to get MAC address of %s: %s", ! 771: ifa->ifa_name, ! 772: strerror(errno)); ! 773: error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg); ! 774: goto error; ! 775: } ! 776: ! 777: mac_addr = (unsigned char *) &ifr.ifr_hwaddr.sa_data; ! 778: ! 779: if (asprintf(&info->value->hardware_address, ! 780: "%02x:%02x:%02x:%02x:%02x:%02x", ! 781: (int) mac_addr[0], (int) mac_addr[1], ! 782: (int) mac_addr[2], (int) mac_addr[3], ! 783: (int) mac_addr[4], (int) mac_addr[5]) == -1) { ! 784: snprintf(err_msg, sizeof(err_msg), ! 785: "failed to format MAC: %s", strerror(errno)); ! 786: error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg); ! 787: goto error; ! 788: } ! 789: ! 790: info->value->has_hardware_address = true; ! 791: close(sock); ! 792: } ! 793: ! 794: if (ifa->ifa_addr && ! 795: ifa->ifa_addr->sa_family == AF_INET) { ! 796: /* interface with IPv4 address */ ! 797: address_item = g_malloc0(sizeof(*address_item)); ! 798: address_item->value = g_malloc0(sizeof(*address_item->value)); ! 799: p = &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr; ! 800: if (!inet_ntop(AF_INET, p, addr4, sizeof(addr4))) { ! 801: snprintf(err_msg, sizeof(err_msg), ! 802: "inet_ntop failed : %s", strerror(errno)); ! 803: error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg); ! 804: goto error; ! 805: } ! 806: ! 807: address_item->value->ip_address = g_strdup(addr4); ! 808: address_item->value->ip_address_type = GUEST_IP_ADDRESS_TYPE_IPV4; ! 809: ! 810: if (ifa->ifa_netmask) { ! 811: /* Count the number of set bits in netmask. ! 812: * This is safe as '1' and '0' cannot be shuffled in netmask. */ ! 813: p = &((struct sockaddr_in *)ifa->ifa_netmask)->sin_addr; ! 814: address_item->value->prefix = ctpop32(((uint32_t *) p)[0]); ! 815: } ! 816: } else if (ifa->ifa_addr && ! 817: ifa->ifa_addr->sa_family == AF_INET6) { ! 818: /* interface with IPv6 address */ ! 819: address_item = g_malloc0(sizeof(*address_item)); ! 820: address_item->value = g_malloc0(sizeof(*address_item->value)); ! 821: p = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr; ! 822: if (!inet_ntop(AF_INET6, p, addr6, sizeof(addr6))) { ! 823: snprintf(err_msg, sizeof(err_msg), ! 824: "inet_ntop failed : %s", strerror(errno)); ! 825: error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg); ! 826: goto error; ! 827: } ! 828: ! 829: address_item->value->ip_address = g_strdup(addr6); ! 830: address_item->value->ip_address_type = GUEST_IP_ADDRESS_TYPE_IPV6; ! 831: ! 832: if (ifa->ifa_netmask) { ! 833: /* Count the number of set bits in netmask. ! 834: * This is safe as '1' and '0' cannot be shuffled in netmask. */ ! 835: p = &((struct sockaddr_in6 *)ifa->ifa_netmask)->sin6_addr; ! 836: address_item->value->prefix = ! 837: ctpop32(((uint32_t *) p)[0]) + ! 838: ctpop32(((uint32_t *) p)[1]) + ! 839: ctpop32(((uint32_t *) p)[2]) + ! 840: ctpop32(((uint32_t *) p)[3]); ! 841: } ! 842: } ! 843: ! 844: if (!address_item) { ! 845: continue; ! 846: } ! 847: ! 848: address_list = &info->value->ip_addresses; ! 849: ! 850: while (*address_list && (*address_list)->next) { ! 851: address_list = &(*address_list)->next; ! 852: } ! 853: ! 854: if (!*address_list) { ! 855: *address_list = address_item; ! 856: } else { ! 857: (*address_list)->next = address_item; ! 858: } ! 859: ! 860: info->value->has_ip_addresses = true; ! 861: ! 862: ! 863: } ! 864: ! 865: freeifaddrs(ifap); ! 866: return head; ! 867: ! 868: error: ! 869: freeifaddrs(ifap); ! 870: qapi_free_GuestNetworkInterfaceList(head); ! 871: return NULL; ! 872: } ! 873: ! 874: #else /* defined(__linux__) */ ! 875: ! 876: void qmp_guest_suspend_disk(Error **err) ! 877: { ! 878: error_set(err, QERR_UNSUPPORTED); ! 879: } ! 880: ! 881: void qmp_guest_suspend_ram(Error **err) ! 882: { ! 883: error_set(err, QERR_UNSUPPORTED); ! 884: } ! 885: ! 886: void qmp_guest_suspend_hybrid(Error **err) ! 887: { ! 888: error_set(err, QERR_UNSUPPORTED); ! 889: } ! 890: ! 891: GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp) ! 892: { ! 893: error_set(errp, QERR_UNSUPPORTED); ! 894: return NULL; ! 895: } ! 896: ! 897: #endif ! 898: ! 899: #if !defined(CONFIG_FSFREEZE) ! 900: ! 901: GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **err) ! 902: { ! 903: error_set(err, QERR_UNSUPPORTED); ! 904: ! 905: return 0; ! 906: } ! 907: ! 908: int64_t qmp_guest_fsfreeze_freeze(Error **err) ! 909: { ! 910: error_set(err, QERR_UNSUPPORTED); ! 911: ! 912: return 0; ! 913: } ! 914: ! 915: int64_t qmp_guest_fsfreeze_thaw(Error **err) ! 916: { ! 917: error_set(err, QERR_UNSUPPORTED); ! 918: ! 919: return 0; ! 920: } ! 921: ! 922: #endif ! 923: ! 924: /* register init/cleanup routines for stateful command groups */ ! 925: void ga_command_state_init(GAState *s, GACommandState *cs) ! 926: { ! 927: #if defined(CONFIG_FSFREEZE) ! 928: ga_command_state_add(cs, NULL, guest_fsfreeze_cleanup); ! 929: #endif ! 930: ga_command_state_add(cs, guest_file_init, NULL); ! 931: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.