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