Annotation of qemu/qga/guest-agent-commands.c, revision 1.1

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: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.