2 * QEMU Guest Agent POSIX-specific command implementations
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.
15 #if defined(__linux__)
19 #if defined(__linux__) && defined(FIFREEZE)
20 #define CONFIG_FSFREEZE
24 #include <sys/types.h>
25 #include <sys/ioctl.h>
27 #include "qga/guest-agent-core.h"
28 #include "qga-qmp-commands.h"
30 #include "qemu-queue.h"
32 static GAState
*ga_state
;
34 static void reopen_fd_to_null(int fd
)
38 nullfd
= open("/dev/null", O_RDWR
);
50 void qmp_guest_shutdown(bool has_mode
, const char *mode
, Error
**err
)
53 const char *shutdown_flag
;
55 slog("guest-shutdown called, mode: %s", mode
);
56 if (!has_mode
|| strcmp(mode
, "powerdown") == 0) {
58 } else if (strcmp(mode
, "halt") == 0) {
60 } else if (strcmp(mode
, "reboot") == 0) {
63 error_set(err
, QERR_INVALID_PARAMETER_VALUE
, "mode",
64 "halt|powerdown|reboot");
70 /* child, start the shutdown */
76 ret
= execl("/sbin/shutdown", "shutdown", shutdown_flag
, "+0",
77 "hypervisor initiated shutdown", (char*)NULL
);
79 slog("guest-shutdown failed: %s", strerror(errno
));
83 error_set(err
, QERR_UNDEFINED_ERROR
);
87 typedef struct GuestFileHandle
{
90 QTAILQ_ENTRY(GuestFileHandle
) next
;
94 QTAILQ_HEAD(, GuestFileHandle
) filehandles
;
97 static void guest_file_handle_add(FILE *fh
)
101 gfh
= g_malloc0(sizeof(GuestFileHandle
));
102 gfh
->id
= fileno(fh
);
104 QTAILQ_INSERT_TAIL(&guest_file_state
.filehandles
, gfh
, next
);
107 static GuestFileHandle
*guest_file_handle_find(int64_t id
)
109 GuestFileHandle
*gfh
;
111 QTAILQ_FOREACH(gfh
, &guest_file_state
.filehandles
, next
)
121 int64_t qmp_guest_file_open(const char *path
, bool has_mode
, const char *mode
, Error
**err
)
130 slog("guest-file-open called, filepath: %s, mode: %s", path
, mode
);
131 fh
= fopen(path
, mode
);
133 error_set(err
, QERR_OPEN_FILE_FAILED
, path
);
137 /* set fd non-blocking to avoid common use cases (like reading from a
138 * named pipe) from hanging the agent
141 ret
= fcntl(fd
, F_GETFL
);
142 ret
= fcntl(fd
, F_SETFL
, ret
| O_NONBLOCK
);
144 error_set(err
, QERR_QGA_COMMAND_FAILED
, "fcntl() failed");
149 guest_file_handle_add(fh
);
150 slog("guest-file-open, handle: %d", fd
);
154 void qmp_guest_file_close(int64_t handle
, Error
**err
)
156 GuestFileHandle
*gfh
= guest_file_handle_find(handle
);
159 slog("guest-file-close called, handle: %ld", handle
);
161 error_set(err
, QERR_FD_NOT_FOUND
, "handle");
165 ret
= fclose(gfh
->fh
);
167 error_set(err
, QERR_QGA_COMMAND_FAILED
, "fclose() failed");
171 QTAILQ_REMOVE(&guest_file_state
.filehandles
, gfh
, next
);
175 struct GuestFileRead
*qmp_guest_file_read(int64_t handle
, bool has_count
,
176 int64_t count
, Error
**err
)
178 GuestFileHandle
*gfh
= guest_file_handle_find(handle
);
179 GuestFileRead
*read_data
= NULL
;
185 error_set(err
, QERR_FD_NOT_FOUND
, "handle");
190 count
= QGA_READ_COUNT_DEFAULT
;
191 } else if (count
< 0) {
192 error_set(err
, QERR_INVALID_PARAMETER
, "count");
197 buf
= g_malloc0(count
+1);
198 read_count
= fread(buf
, 1, count
, fh
);
200 slog("guest-file-read failed, handle: %ld", handle
);
201 error_set(err
, QERR_QGA_COMMAND_FAILED
, "fread() failed");
204 read_data
= g_malloc0(sizeof(GuestFileRead
));
205 read_data
->count
= read_count
;
206 read_data
->eof
= feof(fh
);
208 read_data
->buf_b64
= g_base64_encode(buf
, read_count
);
217 GuestFileWrite
*qmp_guest_file_write(int64_t handle
, const char *buf_b64
,
218 bool has_count
, int64_t count
, Error
**err
)
220 GuestFileWrite
*write_data
= NULL
;
224 GuestFileHandle
*gfh
= guest_file_handle_find(handle
);
228 error_set(err
, QERR_FD_NOT_FOUND
, "handle");
233 buf
= g_base64_decode(buf_b64
, &buf_len
);
237 } else if (count
< 0 || count
> buf_len
) {
239 error_set(err
, QERR_INVALID_PARAMETER
, "count");
243 write_count
= fwrite(buf
, 1, count
, fh
);
245 slog("guest-file-write failed, handle: %ld", handle
);
246 error_set(err
, QERR_QGA_COMMAND_FAILED
, "fwrite() error");
248 write_data
= g_malloc0(sizeof(GuestFileWrite
));
249 write_data
->count
= write_count
;
250 write_data
->eof
= feof(fh
);
258 struct GuestFileSeek
*qmp_guest_file_seek(int64_t handle
, int64_t offset
,
259 int64_t whence
, Error
**err
)
261 GuestFileHandle
*gfh
= guest_file_handle_find(handle
);
262 GuestFileSeek
*seek_data
= NULL
;
267 error_set(err
, QERR_FD_NOT_FOUND
, "handle");
272 ret
= fseek(fh
, offset
, whence
);
274 error_set(err
, QERR_QGA_COMMAND_FAILED
, strerror(errno
));
276 seek_data
= g_malloc0(sizeof(GuestFileRead
));
277 seek_data
->position
= ftell(fh
);
278 seek_data
->eof
= feof(fh
);
285 void qmp_guest_file_flush(int64_t handle
, Error
**err
)
287 GuestFileHandle
*gfh
= guest_file_handle_find(handle
);
292 error_set(err
, QERR_FD_NOT_FOUND
, "handle");
299 error_set(err
, QERR_QGA_COMMAND_FAILED
, strerror(errno
));
303 static void guest_file_init(void)
305 QTAILQ_INIT(&guest_file_state
.filehandles
);
308 #if defined(CONFIG_FSFREEZE)
309 static void disable_logging(void)
311 ga_disable_logging(ga_state
);
314 static void enable_logging(void)
316 ga_enable_logging(ga_state
);
319 typedef struct GuestFsfreezeMount
{
322 QTAILQ_ENTRY(GuestFsfreezeMount
) next
;
323 } GuestFsfreezeMount
;
326 GuestFsfreezeStatus status
;
327 QTAILQ_HEAD(, GuestFsfreezeMount
) mount_list
;
328 } guest_fsfreeze_state
;
331 * Walk the mount table and build a list of local file systems
333 static int guest_fsfreeze_build_mount_list(void)
336 GuestFsfreezeMount
*mount
, *temp
;
337 char const *mtab
= MOUNTED
;
340 QTAILQ_FOREACH_SAFE(mount
, &guest_fsfreeze_state
.mount_list
, next
, temp
) {
341 QTAILQ_REMOVE(&guest_fsfreeze_state
.mount_list
, mount
, next
);
342 g_free(mount
->dirname
);
343 g_free(mount
->devtype
);
347 fp
= setmntent(mtab
, "r");
349 g_warning("fsfreeze: unable to read mtab");
353 while ((ment
= getmntent(fp
))) {
355 * An entry which device name doesn't start with a '/' is
356 * either a dummy file system or a network file system.
357 * Add special handling for smbfs and cifs as is done by
360 if ((ment
->mnt_fsname
[0] != '/') ||
361 (strcmp(ment
->mnt_type
, "smbfs") == 0) ||
362 (strcmp(ment
->mnt_type
, "cifs") == 0)) {
366 mount
= g_malloc0(sizeof(GuestFsfreezeMount
));
367 mount
->dirname
= g_strdup(ment
->mnt_dir
);
368 mount
->devtype
= g_strdup(ment
->mnt_type
);
370 QTAILQ_INSERT_TAIL(&guest_fsfreeze_state
.mount_list
, mount
, next
);
379 * Return status of freeze/thaw
381 GuestFsfreezeStatus
qmp_guest_fsfreeze_status(Error
**err
)
383 return guest_fsfreeze_state
.status
;
387 * Walk list of mounted file systems in the guest, and freeze the ones which
388 * are real local file systems.
390 int64_t qmp_guest_fsfreeze_freeze(Error
**err
)
393 struct GuestFsfreezeMount
*mount
, *temp
;
397 slog("guest-fsfreeze called");
399 if (guest_fsfreeze_state
.status
== GUEST_FSFREEZE_STATUS_FROZEN
) {
403 ret
= guest_fsfreeze_build_mount_list();
408 /* cannot risk guest agent blocking itself on a write in this state */
411 QTAILQ_FOREACH_SAFE(mount
, &guest_fsfreeze_state
.mount_list
, next
, temp
) {
412 fd
= qemu_open(mount
->dirname
, O_RDONLY
);
414 sprintf(err_msg
, "failed to open %s, %s", mount
->dirname
, strerror(errno
));
415 error_set(err
, QERR_QGA_COMMAND_FAILED
, err_msg
);
419 /* we try to cull filesytems we know won't work in advance, but other
420 * filesytems may not implement fsfreeze for less obvious reasons.
421 * these will report EOPNOTSUPP, so we simply ignore them. when
422 * thawing, these filesystems will return an EINVAL instead, due to
423 * not being in a frozen state. Other filesystem-specific
424 * errors may result in EINVAL, however, so the user should check the
425 * number * of filesystems returned here against those returned by the
426 * thaw operation to determine whether everything completed
429 ret
= ioctl(fd
, FIFREEZE
);
430 if (ret
< 0 && errno
!= EOPNOTSUPP
) {
431 sprintf(err_msg
, "failed to freeze %s, %s", mount
->dirname
, strerror(errno
));
432 error_set(err
, QERR_QGA_COMMAND_FAILED
, err_msg
);
441 guest_fsfreeze_state
.status
= GUEST_FSFREEZE_STATUS_FROZEN
;
446 qmp_guest_fsfreeze_thaw(NULL
);
452 * Walk list of frozen file systems in the guest, and thaw them.
454 int64_t qmp_guest_fsfreeze_thaw(Error
**err
)
457 GuestFsfreezeMount
*mount
, *temp
;
459 bool has_error
= false;
461 QTAILQ_FOREACH_SAFE(mount
, &guest_fsfreeze_state
.mount_list
, next
, temp
) {
462 fd
= qemu_open(mount
->dirname
, O_RDONLY
);
467 ret
= ioctl(fd
, FITHAW
);
468 if (ret
< 0 && errno
!= EOPNOTSUPP
&& errno
!= EINVAL
) {
478 guest_fsfreeze_state
.status
= GUEST_FSFREEZE_STATUS_ERROR
;
480 guest_fsfreeze_state
.status
= GUEST_FSFREEZE_STATUS_THAWED
;
486 static void guest_fsfreeze_init(void)
488 guest_fsfreeze_state
.status
= GUEST_FSFREEZE_STATUS_THAWED
;
489 QTAILQ_INIT(&guest_fsfreeze_state
.mount_list
);
492 static void guest_fsfreeze_cleanup(void)
497 if (guest_fsfreeze_state
.status
== GUEST_FSFREEZE_STATUS_FROZEN
) {
498 ret
= qmp_guest_fsfreeze_thaw(&err
);
499 if (ret
< 0 || err
) {
500 slog("failed to clean up frozen filesystems");
506 * Return status of freeze/thaw
508 GuestFsfreezeStatus
qmp_guest_fsfreeze_status(Error
**err
)
510 error_set(err
, QERR_UNSUPPORTED
);
516 * Walk list of mounted file systems in the guest, and freeze the ones which
517 * are real local file systems.
519 int64_t qmp_guest_fsfreeze_freeze(Error
**err
)
521 error_set(err
, QERR_UNSUPPORTED
);
527 * Walk list of frozen file systems in the guest, and thaw them.
529 int64_t qmp_guest_fsfreeze_thaw(Error
**err
)
531 error_set(err
, QERR_UNSUPPORTED
);
537 #define LINUX_SYS_STATE_FILE "/sys/power/state"
538 #define SUSPEND_SUPPORTED 0
539 #define SUSPEND_NOT_SUPPORTED 1
542 * This function forks twice and the information about the mode support
543 * status is passed to the qemu-ga process via a pipe.
545 * This approach allows us to keep the way we reap terminated children
546 * in qemu-ga quite simple.
548 static void bios_supports_mode(const char *pmutils_bin
, const char *pmutils_arg
,
549 const char *sysfile_str
, Error
**err
)
554 int status
, pipefds
[2];
556 if (pipe(pipefds
) < 0) {
557 error_set(err
, QERR_UNDEFINED_ERROR
);
561 pmutils_path
= g_find_program_in_path(pmutils_bin
);
565 struct sigaction act
;
567 memset(&act
, 0, sizeof(act
));
568 act
.sa_handler
= SIG_DFL
;
569 sigaction(SIGCHLD
, &act
, NULL
);
573 reopen_fd_to_null(0);
574 reopen_fd_to_null(1);
575 reopen_fd_to_null(2);
580 char buf
[32]; /* hopefully big enough */
583 execle(pmutils_path
, pmutils_bin
, pmutils_arg
, NULL
, environ
);
587 * If we get here either pm-utils is not installed or execle() has
588 * failed. Let's try the manual method if the caller wants it.
592 _exit(SUSPEND_NOT_SUPPORTED
);
595 fd
= open(LINUX_SYS_STATE_FILE
, O_RDONLY
);
597 _exit(SUSPEND_NOT_SUPPORTED
);
600 ret
= read(fd
, buf
, sizeof(buf
)-1);
602 _exit(SUSPEND_NOT_SUPPORTED
);
606 if (strstr(buf
, sysfile_str
)) {
607 _exit(SUSPEND_SUPPORTED
);
610 _exit(SUSPEND_NOT_SUPPORTED
);
616 status
= SUSPEND_NOT_SUPPORTED
;
619 ret
= write(pipefds
[1], &status
, sizeof(status
));
620 if (ret
!= sizeof(status
)) {
628 g_free(pmutils_path
);
631 error_set(err
, QERR_UNDEFINED_ERROR
);
635 ret
= read(pipefds
[0], &status
, sizeof(status
));
636 if (ret
== sizeof(status
) && WIFEXITED(status
) &&
637 WEXITSTATUS(status
) == SUSPEND_SUPPORTED
) {
641 error_set(err
, QERR_UNSUPPORTED
);
647 static void guest_suspend(const char *pmutils_bin
, const char *sysfile_str
,
653 pmutils_path
= g_find_program_in_path(pmutils_bin
);
661 reopen_fd_to_null(0);
662 reopen_fd_to_null(1);
663 reopen_fd_to_null(2);
666 execle(pmutils_path
, pmutils_bin
, NULL
, environ
);
670 * If we get here either pm-utils is not installed or execle() has
671 * failed. Let's try the manual method if the caller wants it.
678 fd
= open(LINUX_SYS_STATE_FILE
, O_WRONLY
);
683 if (write(fd
, sysfile_str
, strlen(sysfile_str
)) < 0) {
690 g_free(pmutils_path
);
693 error_set(err
, QERR_UNDEFINED_ERROR
);
698 void qmp_guest_suspend_disk(Error
**err
)
700 bios_supports_mode("pm-is-supported", "--hibernate", "disk", err
);
701 if (error_is_set(err
)) {
705 guest_suspend("pm-hibernate", "disk", err
);
708 void qmp_guest_suspend_ram(Error
**err
)
710 bios_supports_mode("pm-is-supported", "--suspend", "mem", err
);
711 if (error_is_set(err
)) {
715 guest_suspend("pm-suspend", "mem", err
);
718 void qmp_guest_suspend_hybrid(Error
**err
)
720 bios_supports_mode("pm-is-supported", "--suspend-hybrid", NULL
, err
);
721 if (error_is_set(err
)) {
725 guest_suspend("pm-suspend-hybrid", NULL
, err
);
728 /* register init/cleanup routines for stateful command groups */
729 void ga_command_state_init(GAState
*s
, GACommandState
*cs
)
732 #if defined(CONFIG_FSFREEZE)
733 ga_command_state_add(cs
, guest_fsfreeze_init
, guest_fsfreeze_cleanup
);
735 ga_command_state_add(cs
, guest_file_init
, NULL
);