2 * QEMU Guest Agent commands
4 * Copyright IBM Corp. 2011
7 * Michael Roth <mdroth@linux.vnet.ibm.com>
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.
13 #if defined(__linux__)
14 #define CONFIG_FSFREEZE
18 #if defined(CONFIG_FSFREEZE)
22 #include <sys/types.h>
23 #include <sys/ioctl.h>
24 #include "qga/guest-agent-core.h"
25 #include "qga-qmp-commands.h"
27 #include "qemu-queue.h"
29 static GAState
*ga_state
;
31 /* Note: in some situations, like with the fsfreeze, logging may be
32 * temporarilly disabled. if it is necessary that a command be able
33 * to log for accounting purposes, check ga_logging_enabled() beforehand,
34 * and use the QERR_QGA_LOGGING_DISABLED to generate an error
36 static void slog(const char *fmt
, ...)
41 g_logv("syslog", G_LOG_LEVEL_INFO
, fmt
, ap
);
45 int64_t qmp_guest_sync(int64_t id
, Error
**errp
)
50 void qmp_guest_ping(Error
**err
)
52 slog("guest-ping called");
55 struct GuestAgentInfo
*qmp_guest_info(Error
**err
)
57 GuestAgentInfo
*info
= qemu_mallocz(sizeof(GuestAgentInfo
));
59 info
->version
= g_strdup(QGA_VERSION
);
64 void qmp_guest_shutdown(bool has_mode
, const char *mode
, Error
**err
)
67 const char *shutdown_flag
;
69 slog("guest-shutdown called, mode: %s", mode
);
70 if (!has_mode
|| strcmp(mode
, "powerdown") == 0) {
72 } else if (strcmp(mode
, "halt") == 0) {
74 } else if (strcmp(mode
, "reboot") == 0) {
77 error_set(err
, QERR_INVALID_PARAMETER_VALUE
, "mode",
78 "halt|powerdown|reboot");
84 /* child, start the shutdown */
90 ret
= execl("/sbin/shutdown", "shutdown", shutdown_flag
, "+0",
91 "hypervisor initiated shutdown", (char*)NULL
);
93 slog("guest-shutdown failed: %s", strerror(errno
));
97 error_set(err
, QERR_UNDEFINED_ERROR
);
101 typedef struct GuestFileHandle
{
104 QTAILQ_ENTRY(GuestFileHandle
) next
;
108 QTAILQ_HEAD(, GuestFileHandle
) filehandles
;
111 static void guest_file_handle_add(FILE *fh
)
113 GuestFileHandle
*gfh
;
115 gfh
= qemu_mallocz(sizeof(GuestFileHandle
));
116 gfh
->id
= fileno(fh
);
118 QTAILQ_INSERT_TAIL(&guest_file_state
.filehandles
, gfh
, next
);
121 static GuestFileHandle
*guest_file_handle_find(int64_t id
)
123 GuestFileHandle
*gfh
;
125 QTAILQ_FOREACH(gfh
, &guest_file_state
.filehandles
, next
)
135 int64_t qmp_guest_file_open(const char *path
, bool has_mode
, const char *mode
, Error
**err
)
144 slog("guest-file-open called, filepath: %s, mode: %s", path
, mode
);
145 fh
= fopen(path
, mode
);
147 error_set(err
, QERR_OPEN_FILE_FAILED
, path
);
151 /* set fd non-blocking to avoid common use cases (like reading from a
152 * named pipe) from hanging the agent
155 ret
= fcntl(fd
, F_GETFL
);
156 ret
= fcntl(fd
, F_SETFL
, ret
| O_NONBLOCK
);
158 error_set(err
, QERR_QGA_COMMAND_FAILED
, "fcntl() failed");
163 guest_file_handle_add(fh
);
164 slog("guest-file-open, handle: %d", fd
);
168 void qmp_guest_file_close(int64_t handle
, Error
**err
)
170 GuestFileHandle
*gfh
= guest_file_handle_find(handle
);
173 slog("guest-file-close called, handle: %ld", handle
);
175 error_set(err
, QERR_FD_NOT_FOUND
, "handle");
179 ret
= fclose(gfh
->fh
);
181 error_set(err
, QERR_QGA_COMMAND_FAILED
, "fclose() failed");
185 QTAILQ_REMOVE(&guest_file_state
.filehandles
, gfh
, next
);
189 struct GuestFileRead
*qmp_guest_file_read(int64_t handle
, bool has_count
,
190 int64_t count
, Error
**err
)
192 GuestFileHandle
*gfh
= guest_file_handle_find(handle
);
193 GuestFileRead
*read_data
= NULL
;
199 error_set(err
, QERR_FD_NOT_FOUND
, "handle");
204 count
= QGA_READ_COUNT_DEFAULT
;
205 } else if (count
< 0) {
206 error_set(err
, QERR_INVALID_PARAMETER
, "count");
211 buf
= qemu_mallocz(count
+1);
212 read_count
= fread(buf
, 1, count
, fh
);
214 slog("guest-file-read failed, handle: %ld", handle
);
215 error_set(err
, QERR_QGA_COMMAND_FAILED
, "fread() failed");
218 read_data
= qemu_mallocz(sizeof(GuestFileRead
));
219 read_data
->count
= read_count
;
220 read_data
->eof
= feof(fh
);
222 read_data
->buf_b64
= g_base64_encode(buf
, read_count
);
231 GuestFileWrite
*qmp_guest_file_write(int64_t handle
, const char *buf_b64
,
232 bool has_count
, int64_t count
, Error
**err
)
234 GuestFileWrite
*write_data
= NULL
;
238 GuestFileHandle
*gfh
= guest_file_handle_find(handle
);
242 error_set(err
, QERR_FD_NOT_FOUND
, "handle");
247 buf
= g_base64_decode(buf_b64
, &buf_len
);
251 } else if (count
< 0 || count
> buf_len
) {
253 error_set(err
, QERR_INVALID_PARAMETER
, "count");
257 write_count
= fwrite(buf
, 1, count
, fh
);
259 slog("guest-file-write failed, handle: %ld", handle
);
260 error_set(err
, QERR_QGA_COMMAND_FAILED
, "fwrite() error");
262 write_data
= qemu_mallocz(sizeof(GuestFileWrite
));
263 write_data
->count
= write_count
;
264 write_data
->eof
= feof(fh
);
272 struct GuestFileSeek
*qmp_guest_file_seek(int64_t handle
, int64_t offset
,
273 int64_t whence
, Error
**err
)
275 GuestFileHandle
*gfh
= guest_file_handle_find(handle
);
276 GuestFileSeek
*seek_data
= NULL
;
281 error_set(err
, QERR_FD_NOT_FOUND
, "handle");
286 ret
= fseek(fh
, offset
, whence
);
288 error_set(err
, QERR_QGA_COMMAND_FAILED
, strerror(errno
));
290 seek_data
= qemu_mallocz(sizeof(GuestFileRead
));
291 seek_data
->position
= ftell(fh
);
292 seek_data
->eof
= feof(fh
);
299 void qmp_guest_file_flush(int64_t handle
, Error
**err
)
301 GuestFileHandle
*gfh
= guest_file_handle_find(handle
);
306 error_set(err
, QERR_FD_NOT_FOUND
, "handle");
313 error_set(err
, QERR_QGA_COMMAND_FAILED
, strerror(errno
));
317 static void guest_file_init(void)
319 QTAILQ_INIT(&guest_file_state
.filehandles
);
322 #if defined(CONFIG_FSFREEZE)
323 static void disable_logging(void)
325 ga_disable_logging(ga_state
);
328 static void enable_logging(void)
330 ga_enable_logging(ga_state
);
333 typedef struct GuestFsfreezeMount
{
336 QTAILQ_ENTRY(GuestFsfreezeMount
) next
;
337 } GuestFsfreezeMount
;
340 GuestFsfreezeStatus status
;
341 QTAILQ_HEAD(, GuestFsfreezeMount
) mount_list
;
342 } guest_fsfreeze_state
;
345 * Walk the mount table and build a list of local file systems
347 static int guest_fsfreeze_build_mount_list(void)
350 GuestFsfreezeMount
*mount
, *temp
;
351 char const *mtab
= MOUNTED
;
354 QTAILQ_FOREACH_SAFE(mount
, &guest_fsfreeze_state
.mount_list
, next
, temp
) {
355 QTAILQ_REMOVE(&guest_fsfreeze_state
.mount_list
, mount
, next
);
356 qemu_free(mount
->dirname
);
357 qemu_free(mount
->devtype
);
361 fp
= setmntent(mtab
, "r");
363 g_warning("fsfreeze: unable to read mtab");
367 while ((ment
= getmntent(fp
))) {
369 * An entry which device name doesn't start with a '/' is
370 * either a dummy file system or a network file system.
371 * Add special handling for smbfs and cifs as is done by
374 if ((ment
->mnt_fsname
[0] != '/') ||
375 (strcmp(ment
->mnt_type
, "smbfs") == 0) ||
376 (strcmp(ment
->mnt_type
, "cifs") == 0)) {
380 mount
= qemu_mallocz(sizeof(GuestFsfreezeMount
));
381 mount
->dirname
= qemu_strdup(ment
->mnt_dir
);
382 mount
->devtype
= qemu_strdup(ment
->mnt_type
);
384 QTAILQ_INSERT_TAIL(&guest_fsfreeze_state
.mount_list
, mount
, next
);
393 * Return status of freeze/thaw
395 GuestFsfreezeStatus
qmp_guest_fsfreeze_status(Error
**err
)
397 return guest_fsfreeze_state
.status
;
401 * Walk list of mounted file systems in the guest, and freeze the ones which
402 * are real local file systems.
404 int64_t qmp_guest_fsfreeze_freeze(Error
**err
)
407 struct GuestFsfreezeMount
*mount
, *temp
;
411 slog("guest-fsfreeze called");
413 if (guest_fsfreeze_state
.status
== GUEST_FSFREEZE_STATUS_FROZEN
) {
417 ret
= guest_fsfreeze_build_mount_list();
422 /* cannot risk guest agent blocking itself on a write in this state */
425 QTAILQ_FOREACH_SAFE(mount
, &guest_fsfreeze_state
.mount_list
, next
, temp
) {
426 fd
= qemu_open(mount
->dirname
, O_RDONLY
);
428 sprintf(err_msg
, "failed to open %s, %s", mount
->dirname
, strerror(errno
));
429 error_set(err
, QERR_QGA_COMMAND_FAILED
, err_msg
);
433 /* we try to cull filesytems we know won't work in advance, but other
434 * filesytems may not implement fsfreeze for less obvious reasons.
435 * these will report EOPNOTSUPP, so we simply ignore them. when
436 * thawing, these filesystems will return an EINVAL instead, due to
437 * not being in a frozen state. Other filesystem-specific
438 * errors may result in EINVAL, however, so the user should check the
439 * number * of filesystems returned here against those returned by the
440 * thaw operation to determine whether everything completed
443 ret
= ioctl(fd
, FIFREEZE
);
444 if (ret
< 0 && errno
!= EOPNOTSUPP
) {
445 sprintf(err_msg
, "failed to freeze %s, %s", mount
->dirname
, strerror(errno
));
446 error_set(err
, QERR_QGA_COMMAND_FAILED
, err_msg
);
455 guest_fsfreeze_state
.status
= GUEST_FSFREEZE_STATUS_FROZEN
;
460 qmp_guest_fsfreeze_thaw(NULL
);
466 * Walk list of frozen file systems in the guest, and thaw them.
468 int64_t qmp_guest_fsfreeze_thaw(Error
**err
)
471 GuestFsfreezeMount
*mount
, *temp
;
473 bool has_error
= false;
475 QTAILQ_FOREACH_SAFE(mount
, &guest_fsfreeze_state
.mount_list
, next
, temp
) {
476 fd
= qemu_open(mount
->dirname
, O_RDONLY
);
481 ret
= ioctl(fd
, FITHAW
);
482 if (ret
< 0 && errno
!= EOPNOTSUPP
&& errno
!= EINVAL
) {
492 guest_fsfreeze_state
.status
= GUEST_FSFREEZE_STATUS_ERROR
;
494 guest_fsfreeze_state
.status
= GUEST_FSFREEZE_STATUS_THAWED
;
500 static void guest_fsfreeze_init(void)
502 guest_fsfreeze_state
.status
= GUEST_FSFREEZE_STATUS_THAWED
;
503 QTAILQ_INIT(&guest_fsfreeze_state
.mount_list
);
506 static void guest_fsfreeze_cleanup(void)
511 if (guest_fsfreeze_state
.status
== GUEST_FSFREEZE_STATUS_FROZEN
) {
512 ret
= qmp_guest_fsfreeze_thaw(&err
);
513 if (ret
< 0 || err
) {
514 slog("failed to clean up frozen filesystems");
520 * Return status of freeze/thaw
522 GuestFsfreezeStatus
qmp_guest_fsfreeze_status(Error
**err
)
524 error_set(err
, QERR_COMMAND_NOT_FOUND
, "guest_fsfreeze_status");
530 * Walk list of mounted file systems in the guest, and freeze the ones which
531 * are real local file systems.
533 int64_t qmp_guest_fsfreeze_freeze(Error
**err
)
535 error_set(err
, QERR_COMMAND_NOT_FOUND
, "guest_fsfreeze_freeze");
541 * Walk list of frozen file systems in the guest, and thaw them.
543 int64_t qmp_guest_fsfreeze_thaw(Error
**err
)
545 error_set(err
, QERR_COMMAND_NOT_FOUND
, "guest_fsfreeze_thaw");
551 /* register init/cleanup routines for stateful command groups */
552 void ga_command_state_init(GAState
*s
, GACommandState
*cs
)
555 #if defined(CONFIG_FSFREEZE)
556 ga_command_state_add(cs
, guest_fsfreeze_init
, guest_fsfreeze_cleanup
);
558 ga_command_state_add(cs
, guest_file_init
, NULL
);