2 * QEMU Guest Agent POSIX-specific command implementations
4 * Copyright IBM Corp. 2011
7 * Michael Roth <mdroth@linux.vnet.ibm.com>
8 * Michal Privoznik <mprivozn@redhat.com>
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.
15 #include <sys/types.h>
16 #include <sys/ioctl.h>
17 #include "qga/guest-agent-core.h"
18 #include "qga-qmp-commands.h"
20 #include "qemu-queue.h"
21 #include "host-utils.h"
23 #if defined(__linux__)
27 #include <arpa/inet.h>
28 #include <sys/socket.h>
32 #if defined(__linux__) && defined(FIFREEZE)
33 #define CONFIG_FSFREEZE
37 void qmp_guest_shutdown(bool has_mode
, const char *mode
, Error
**err
)
40 const char *shutdown_flag
;
42 slog("guest-shutdown called, mode: %s", mode
);
43 if (!has_mode
|| strcmp(mode
, "powerdown") == 0) {
45 } else if (strcmp(mode
, "halt") == 0) {
47 } else if (strcmp(mode
, "reboot") == 0) {
50 error_set(err
, QERR_INVALID_PARAMETER_VALUE
, "mode",
51 "halt|powerdown|reboot");
57 /* child, start the shutdown */
63 ret
= execl("/sbin/shutdown", "shutdown", shutdown_flag
, "+0",
64 "hypervisor initiated shutdown", (char*)NULL
);
66 slog("guest-shutdown failed: %s", strerror(errno
));
70 error_set(err
, QERR_UNDEFINED_ERROR
);
74 typedef struct GuestFileHandle
{
77 QTAILQ_ENTRY(GuestFileHandle
) next
;
81 QTAILQ_HEAD(, GuestFileHandle
) filehandles
;
84 static void guest_file_handle_add(FILE *fh
)
88 gfh
= g_malloc0(sizeof(GuestFileHandle
));
91 QTAILQ_INSERT_TAIL(&guest_file_state
.filehandles
, gfh
, next
);
94 static GuestFileHandle
*guest_file_handle_find(int64_t id
)
98 QTAILQ_FOREACH(gfh
, &guest_file_state
.filehandles
, next
)
108 int64_t qmp_guest_file_open(const char *path
, bool has_mode
, const char *mode
, Error
**err
)
117 slog("guest-file-open called, filepath: %s, mode: %s", path
, mode
);
118 fh
= fopen(path
, mode
);
120 error_set(err
, QERR_OPEN_FILE_FAILED
, path
);
124 /* set fd non-blocking to avoid common use cases (like reading from a
125 * named pipe) from hanging the agent
128 ret
= fcntl(fd
, F_GETFL
);
129 ret
= fcntl(fd
, F_SETFL
, ret
| O_NONBLOCK
);
131 error_set(err
, QERR_QGA_COMMAND_FAILED
, "fcntl() failed");
136 guest_file_handle_add(fh
);
137 slog("guest-file-open, handle: %d", fd
);
141 void qmp_guest_file_close(int64_t handle
, Error
**err
)
143 GuestFileHandle
*gfh
= guest_file_handle_find(handle
);
146 slog("guest-file-close called, handle: %ld", handle
);
148 error_set(err
, QERR_FD_NOT_FOUND
, "handle");
152 ret
= fclose(gfh
->fh
);
154 error_set(err
, QERR_QGA_COMMAND_FAILED
, "fclose() failed");
158 QTAILQ_REMOVE(&guest_file_state
.filehandles
, gfh
, next
);
162 struct GuestFileRead
*qmp_guest_file_read(int64_t handle
, bool has_count
,
163 int64_t count
, Error
**err
)
165 GuestFileHandle
*gfh
= guest_file_handle_find(handle
);
166 GuestFileRead
*read_data
= NULL
;
172 error_set(err
, QERR_FD_NOT_FOUND
, "handle");
177 count
= QGA_READ_COUNT_DEFAULT
;
178 } else if (count
< 0) {
179 error_set(err
, QERR_INVALID_PARAMETER
, "count");
184 buf
= g_malloc0(count
+1);
185 read_count
= fread(buf
, 1, count
, fh
);
187 slog("guest-file-read failed, handle: %ld", handle
);
188 error_set(err
, QERR_QGA_COMMAND_FAILED
, "fread() failed");
191 read_data
= g_malloc0(sizeof(GuestFileRead
));
192 read_data
->count
= read_count
;
193 read_data
->eof
= feof(fh
);
195 read_data
->buf_b64
= g_base64_encode(buf
, read_count
);
204 GuestFileWrite
*qmp_guest_file_write(int64_t handle
, const char *buf_b64
,
205 bool has_count
, int64_t count
, Error
**err
)
207 GuestFileWrite
*write_data
= NULL
;
211 GuestFileHandle
*gfh
= guest_file_handle_find(handle
);
215 error_set(err
, QERR_FD_NOT_FOUND
, "handle");
220 buf
= g_base64_decode(buf_b64
, &buf_len
);
224 } else if (count
< 0 || count
> buf_len
) {
226 error_set(err
, QERR_INVALID_PARAMETER
, "count");
230 write_count
= fwrite(buf
, 1, count
, fh
);
232 slog("guest-file-write failed, handle: %ld", handle
);
233 error_set(err
, QERR_QGA_COMMAND_FAILED
, "fwrite() error");
235 write_data
= g_malloc0(sizeof(GuestFileWrite
));
236 write_data
->count
= write_count
;
237 write_data
->eof
= feof(fh
);
245 struct GuestFileSeek
*qmp_guest_file_seek(int64_t handle
, int64_t offset
,
246 int64_t whence
, Error
**err
)
248 GuestFileHandle
*gfh
= guest_file_handle_find(handle
);
249 GuestFileSeek
*seek_data
= NULL
;
254 error_set(err
, QERR_FD_NOT_FOUND
, "handle");
259 ret
= fseek(fh
, offset
, whence
);
261 error_set(err
, QERR_QGA_COMMAND_FAILED
, strerror(errno
));
263 seek_data
= g_malloc0(sizeof(GuestFileRead
));
264 seek_data
->position
= ftell(fh
);
265 seek_data
->eof
= feof(fh
);
272 void qmp_guest_file_flush(int64_t handle
, Error
**err
)
274 GuestFileHandle
*gfh
= guest_file_handle_find(handle
);
279 error_set(err
, QERR_FD_NOT_FOUND
, "handle");
286 error_set(err
, QERR_QGA_COMMAND_FAILED
, strerror(errno
));
290 static void guest_file_init(void)
292 QTAILQ_INIT(&guest_file_state
.filehandles
);
295 /* linux-specific implementations. avoid this if at all possible. */
296 #if defined(__linux__)
298 #if defined(CONFIG_FSFREEZE)
300 typedef struct GuestFsfreezeMount
{
303 QTAILQ_ENTRY(GuestFsfreezeMount
) next
;
304 } GuestFsfreezeMount
;
306 typedef QTAILQ_HEAD(, GuestFsfreezeMount
) GuestFsfreezeMountList
;
308 static void guest_fsfreeze_free_mount_list(GuestFsfreezeMountList
*mounts
)
310 GuestFsfreezeMount
*mount
, *temp
;
316 QTAILQ_FOREACH_SAFE(mount
, mounts
, next
, temp
) {
317 QTAILQ_REMOVE(mounts
, mount
, next
);
318 g_free(mount
->dirname
);
319 g_free(mount
->devtype
);
325 * Walk the mount table and build a list of local file systems
327 static int guest_fsfreeze_build_mount_list(GuestFsfreezeMountList
*mounts
)
330 GuestFsfreezeMount
*mount
;
331 char const *mtab
= MOUNTED
;
334 fp
= setmntent(mtab
, "r");
336 g_warning("fsfreeze: unable to read mtab");
340 while ((ment
= getmntent(fp
))) {
342 * An entry which device name doesn't start with a '/' is
343 * either a dummy file system or a network file system.
344 * Add special handling for smbfs and cifs as is done by
347 if ((ment
->mnt_fsname
[0] != '/') ||
348 (strcmp(ment
->mnt_type
, "smbfs") == 0) ||
349 (strcmp(ment
->mnt_type
, "cifs") == 0)) {
353 mount
= g_malloc0(sizeof(GuestFsfreezeMount
));
354 mount
->dirname
= g_strdup(ment
->mnt_dir
);
355 mount
->devtype
= g_strdup(ment
->mnt_type
);
357 QTAILQ_INSERT_TAIL(mounts
, mount
, next
);
366 * Return status of freeze/thaw
368 GuestFsfreezeStatus
qmp_guest_fsfreeze_status(Error
**err
)
370 if (ga_is_frozen(ga_state
)) {
371 return GUEST_FSFREEZE_STATUS_FROZEN
;
374 return GUEST_FSFREEZE_STATUS_THAWED
;
378 * Walk list of mounted file systems in the guest, and freeze the ones which
379 * are real local file systems.
381 int64_t qmp_guest_fsfreeze_freeze(Error
**err
)
384 GuestFsfreezeMountList mounts
;
385 struct GuestFsfreezeMount
*mount
;
389 slog("guest-fsfreeze called");
391 QTAILQ_INIT(&mounts
);
392 ret
= guest_fsfreeze_build_mount_list(&mounts
);
397 /* cannot risk guest agent blocking itself on a write in this state */
398 ga_set_frozen(ga_state
);
400 QTAILQ_FOREACH(mount
, &mounts
, next
) {
401 fd
= qemu_open(mount
->dirname
, O_RDONLY
);
403 sprintf(err_msg
, "failed to open %s, %s", mount
->dirname
,
405 error_set(err
, QERR_QGA_COMMAND_FAILED
, err_msg
);
409 /* we try to cull filesytems we know won't work in advance, but other
410 * filesytems may not implement fsfreeze for less obvious reasons.
411 * these will report EOPNOTSUPP. we simply ignore these when tallying
412 * the number of frozen filesystems.
414 * any other error means a failure to freeze a filesystem we
415 * expect to be freezable, so return an error in those cases
416 * and return system to thawed state.
418 ret
= ioctl(fd
, FIFREEZE
);
420 if (errno
!= EOPNOTSUPP
) {
421 sprintf(err_msg
, "failed to freeze %s, %s",
422 mount
->dirname
, strerror(errno
));
423 error_set(err
, QERR_QGA_COMMAND_FAILED
, err_msg
);
433 guest_fsfreeze_free_mount_list(&mounts
);
437 guest_fsfreeze_free_mount_list(&mounts
);
438 qmp_guest_fsfreeze_thaw(NULL
);
443 * Walk list of frozen file systems in the guest, and thaw them.
445 int64_t qmp_guest_fsfreeze_thaw(Error
**err
)
448 GuestFsfreezeMountList mounts
;
449 GuestFsfreezeMount
*mount
;
450 int fd
, i
= 0, logged
;
452 QTAILQ_INIT(&mounts
);
453 ret
= guest_fsfreeze_build_mount_list(&mounts
);
455 error_set(err
, QERR_QGA_COMMAND_FAILED
,
456 "failed to enumerate filesystems");
460 QTAILQ_FOREACH(mount
, &mounts
, next
) {
462 fd
= qemu_open(mount
->dirname
, O_RDONLY
);
466 /* we have no way of knowing whether a filesystem was actually unfrozen
467 * as a result of a successful call to FITHAW, only that if an error
468 * was returned the filesystem was *not* unfrozen by that particular
471 * since multiple preceding FIFREEZEs require multiple calls to FITHAW
472 * to unfreeze, continuing issuing FITHAW until an error is returned,
473 * in which case either the filesystem is in an unfreezable state, or,
474 * more likely, it was thawed previously (and remains so afterward).
476 * also, since the most recent successful call is the one that did
477 * the actual unfreeze, we can use this to provide an accurate count
478 * of the number of filesystems unfrozen by guest-fsfreeze-thaw, which
479 * may * be useful for determining whether a filesystem was unfrozen
480 * during the freeze/thaw phase by a process other than qemu-ga.
483 ret
= ioctl(fd
, FITHAW
);
484 if (ret
== 0 && !logged
) {
492 ga_unset_frozen(ga_state
);
493 guest_fsfreeze_free_mount_list(&mounts
);
497 static void guest_fsfreeze_cleanup(void)
502 if (ga_is_frozen(ga_state
) == GUEST_FSFREEZE_STATUS_FROZEN
) {
503 ret
= qmp_guest_fsfreeze_thaw(&err
);
504 if (ret
< 0 || err
) {
505 slog("failed to clean up frozen filesystems");
509 #endif /* CONFIG_FSFREEZE */
511 #define LINUX_SYS_STATE_FILE "/sys/power/state"
512 #define SUSPEND_SUPPORTED 0
513 #define SUSPEND_NOT_SUPPORTED 1
516 * This function forks twice and the information about the mode support
517 * status is passed to the qemu-ga process via a pipe.
519 * This approach allows us to keep the way we reap terminated children
520 * in qemu-ga quite simple.
522 static void bios_supports_mode(const char *pmutils_bin
, const char *pmutils_arg
,
523 const char *sysfile_str
, Error
**err
)
528 int status
, pipefds
[2];
530 if (pipe(pipefds
) < 0) {
531 error_set(err
, QERR_UNDEFINED_ERROR
);
535 pmutils_path
= g_find_program_in_path(pmutils_bin
);
539 struct sigaction act
;
541 memset(&act
, 0, sizeof(act
));
542 act
.sa_handler
= SIG_DFL
;
543 sigaction(SIGCHLD
, &act
, NULL
);
547 reopen_fd_to_null(0);
548 reopen_fd_to_null(1);
549 reopen_fd_to_null(2);
554 char buf
[32]; /* hopefully big enough */
557 execle(pmutils_path
, pmutils_bin
, pmutils_arg
, NULL
, environ
);
561 * If we get here either pm-utils is not installed or execle() has
562 * failed. Let's try the manual method if the caller wants it.
566 _exit(SUSPEND_NOT_SUPPORTED
);
569 fd
= open(LINUX_SYS_STATE_FILE
, O_RDONLY
);
571 _exit(SUSPEND_NOT_SUPPORTED
);
574 ret
= read(fd
, buf
, sizeof(buf
)-1);
576 _exit(SUSPEND_NOT_SUPPORTED
);
580 if (strstr(buf
, sysfile_str
)) {
581 _exit(SUSPEND_SUPPORTED
);
584 _exit(SUSPEND_NOT_SUPPORTED
);
590 status
= SUSPEND_NOT_SUPPORTED
;
593 ret
= write(pipefds
[1], &status
, sizeof(status
));
594 if (ret
!= sizeof(status
)) {
602 g_free(pmutils_path
);
605 error_set(err
, QERR_UNDEFINED_ERROR
);
609 ret
= read(pipefds
[0], &status
, sizeof(status
));
610 if (ret
== sizeof(status
) && WIFEXITED(status
) &&
611 WEXITSTATUS(status
) == SUSPEND_SUPPORTED
) {
615 error_set(err
, QERR_UNSUPPORTED
);
621 static void guest_suspend(const char *pmutils_bin
, const char *sysfile_str
,
627 pmutils_path
= g_find_program_in_path(pmutils_bin
);
635 reopen_fd_to_null(0);
636 reopen_fd_to_null(1);
637 reopen_fd_to_null(2);
640 execle(pmutils_path
, pmutils_bin
, NULL
, environ
);
644 * If we get here either pm-utils is not installed or execle() has
645 * failed. Let's try the manual method if the caller wants it.
652 fd
= open(LINUX_SYS_STATE_FILE
, O_WRONLY
);
657 if (write(fd
, sysfile_str
, strlen(sysfile_str
)) < 0) {
664 g_free(pmutils_path
);
667 error_set(err
, QERR_UNDEFINED_ERROR
);
672 void qmp_guest_suspend_disk(Error
**err
)
674 bios_supports_mode("pm-is-supported", "--hibernate", "disk", err
);
675 if (error_is_set(err
)) {
679 guest_suspend("pm-hibernate", "disk", err
);
682 void qmp_guest_suspend_ram(Error
**err
)
684 bios_supports_mode("pm-is-supported", "--suspend", "mem", err
);
685 if (error_is_set(err
)) {
689 guest_suspend("pm-suspend", "mem", err
);
692 void qmp_guest_suspend_hybrid(Error
**err
)
694 bios_supports_mode("pm-is-supported", "--suspend-hybrid", NULL
, err
);
695 if (error_is_set(err
)) {
699 guest_suspend("pm-suspend-hybrid", NULL
, err
);
702 static GuestNetworkInterfaceList
*
703 guest_find_interface(GuestNetworkInterfaceList
*head
,
706 for (; head
; head
= head
->next
) {
707 if (strcmp(head
->value
->name
, name
) == 0) {
716 * Build information about guest interfaces
718 GuestNetworkInterfaceList
*qmp_guest_network_get_interfaces(Error
**errp
)
720 GuestNetworkInterfaceList
*head
= NULL
, *cur_item
= NULL
;
721 struct ifaddrs
*ifap
, *ifa
;
724 if (getifaddrs(&ifap
) < 0) {
725 snprintf(err_msg
, sizeof(err_msg
),
726 "getifaddrs failed: %s", strerror(errno
));
727 error_set(errp
, QERR_QGA_COMMAND_FAILED
, err_msg
);
731 for (ifa
= ifap
; ifa
; ifa
= ifa
->ifa_next
) {
732 GuestNetworkInterfaceList
*info
;
733 GuestIpAddressList
**address_list
= NULL
, *address_item
= NULL
;
734 char addr4
[INET_ADDRSTRLEN
];
735 char addr6
[INET6_ADDRSTRLEN
];
738 unsigned char *mac_addr
;
741 g_debug("Processing %s interface", ifa
->ifa_name
);
743 info
= guest_find_interface(head
, ifa
->ifa_name
);
746 info
= g_malloc0(sizeof(*info
));
747 info
->value
= g_malloc0(sizeof(*info
->value
));
748 info
->value
->name
= g_strdup(ifa
->ifa_name
);
751 head
= cur_item
= info
;
753 cur_item
->next
= info
;
758 if (!info
->value
->has_hardware_address
&&
759 ifa
->ifa_flags
& SIOCGIFHWADDR
) {
760 /* we haven't obtained HW address yet */
761 sock
= socket(PF_INET
, SOCK_STREAM
, 0);
763 snprintf(err_msg
, sizeof(err_msg
),
764 "failed to create socket: %s", strerror(errno
));
765 error_set(errp
, QERR_QGA_COMMAND_FAILED
, err_msg
);
769 memset(&ifr
, 0, sizeof(ifr
));
770 strncpy(ifr
.ifr_name
, info
->value
->name
, IF_NAMESIZE
);
771 if (ioctl(sock
, SIOCGIFHWADDR
, &ifr
) == -1) {
772 snprintf(err_msg
, sizeof(err_msg
),
773 "failed to get MAC address of %s: %s",
776 error_set(errp
, QERR_QGA_COMMAND_FAILED
, err_msg
);
780 mac_addr
= (unsigned char *) &ifr
.ifr_hwaddr
.sa_data
;
782 if (asprintf(&info
->value
->hardware_address
,
783 "%02x:%02x:%02x:%02x:%02x:%02x",
784 (int) mac_addr
[0], (int) mac_addr
[1],
785 (int) mac_addr
[2], (int) mac_addr
[3],
786 (int) mac_addr
[4], (int) mac_addr
[5]) == -1) {
787 snprintf(err_msg
, sizeof(err_msg
),
788 "failed to format MAC: %s", strerror(errno
));
789 error_set(errp
, QERR_QGA_COMMAND_FAILED
, err_msg
);
793 info
->value
->has_hardware_address
= true;
798 ifa
->ifa_addr
->sa_family
== AF_INET
) {
799 /* interface with IPv4 address */
800 address_item
= g_malloc0(sizeof(*address_item
));
801 address_item
->value
= g_malloc0(sizeof(*address_item
->value
));
802 p
= &((struct sockaddr_in
*)ifa
->ifa_addr
)->sin_addr
;
803 if (!inet_ntop(AF_INET
, p
, addr4
, sizeof(addr4
))) {
804 snprintf(err_msg
, sizeof(err_msg
),
805 "inet_ntop failed : %s", strerror(errno
));
806 error_set(errp
, QERR_QGA_COMMAND_FAILED
, err_msg
);
810 address_item
->value
->ip_address
= g_strdup(addr4
);
811 address_item
->value
->ip_address_type
= GUEST_IP_ADDRESS_TYPE_IPV4
;
813 if (ifa
->ifa_netmask
) {
814 /* Count the number of set bits in netmask.
815 * This is safe as '1' and '0' cannot be shuffled in netmask. */
816 p
= &((struct sockaddr_in
*)ifa
->ifa_netmask
)->sin_addr
;
817 address_item
->value
->prefix
= ctpop32(((uint32_t *) p
)[0]);
819 } else if (ifa
->ifa_addr
&&
820 ifa
->ifa_addr
->sa_family
== AF_INET6
) {
821 /* interface with IPv6 address */
822 address_item
= g_malloc0(sizeof(*address_item
));
823 address_item
->value
= g_malloc0(sizeof(*address_item
->value
));
824 p
= &((struct sockaddr_in6
*)ifa
->ifa_addr
)->sin6_addr
;
825 if (!inet_ntop(AF_INET6
, p
, addr6
, sizeof(addr6
))) {
826 snprintf(err_msg
, sizeof(err_msg
),
827 "inet_ntop failed : %s", strerror(errno
));
828 error_set(errp
, QERR_QGA_COMMAND_FAILED
, err_msg
);
832 address_item
->value
->ip_address
= g_strdup(addr6
);
833 address_item
->value
->ip_address_type
= GUEST_IP_ADDRESS_TYPE_IPV6
;
835 if (ifa
->ifa_netmask
) {
836 /* Count the number of set bits in netmask.
837 * This is safe as '1' and '0' cannot be shuffled in netmask. */
838 p
= &((struct sockaddr_in6
*)ifa
->ifa_netmask
)->sin6_addr
;
839 address_item
->value
->prefix
=
840 ctpop32(((uint32_t *) p
)[0]) +
841 ctpop32(((uint32_t *) p
)[1]) +
842 ctpop32(((uint32_t *) p
)[2]) +
843 ctpop32(((uint32_t *) p
)[3]);
851 address_list
= &info
->value
->ip_addresses
;
853 while (*address_list
&& (*address_list
)->next
) {
854 address_list
= &(*address_list
)->next
;
857 if (!*address_list
) {
858 *address_list
= address_item
;
860 (*address_list
)->next
= address_item
;
863 info
->value
->has_ip_addresses
= true;
873 qapi_free_GuestNetworkInterfaceList(head
);
877 #else /* defined(__linux__) */
879 void qmp_guest_suspend_disk(Error
**err
)
881 error_set(err
, QERR_UNSUPPORTED
);
884 void qmp_guest_suspend_ram(Error
**err
)
886 error_set(err
, QERR_UNSUPPORTED
);
889 void qmp_guest_suspend_hybrid(Error
**err
)
891 error_set(err
, QERR_UNSUPPORTED
);
894 GuestNetworkInterfaceList
*qmp_guest_network_get_interfaces(Error
**errp
)
896 error_set(errp
, QERR_UNSUPPORTED
);
902 #if !defined(CONFIG_FSFREEZE)
904 GuestFsfreezeStatus
qmp_guest_fsfreeze_status(Error
**err
)
906 error_set(err
, QERR_UNSUPPORTED
);
911 int64_t qmp_guest_fsfreeze_freeze(Error
**err
)
913 error_set(err
, QERR_UNSUPPORTED
);
918 int64_t qmp_guest_fsfreeze_thaw(Error
**err
)
920 error_set(err
, QERR_UNSUPPORTED
);
927 /* register init/cleanup routines for stateful command groups */
928 void ga_command_state_init(GAState
*s
, GACommandState
*cs
)
930 #if defined(CONFIG_FSFREEZE)
931 ga_command_state_add(cs
, NULL
, guest_fsfreeze_cleanup
);
933 ga_command_state_add(cs
, guest_file_init
, NULL
);