qemu/qga/commands-bsd.c
Daniel P. Berrangé 83a7a1ab9a qga: conditionalize schema for commands requiring linux/win32
Some commands were blocked based on CONFIG_FSFREEZE, but their
impl had nothing todo with CONFIG_FSFREEZE, and were instead
either Linux-only, or Win+Linux-only.

Rather than creating stubs for every command that just return
QERR_UNSUPPORTED, use 'if' conditions in the QAPI schema to
fully exclude generation of the stats and fsinfo commands on
platforms that can't support them.

The command will be rejected at QMP dispatch time instead,
avoiding reimplementing rejection by blocking the stub commands.
This changes the error message for affected commands from

    {"class": "CommandNotFound", "desc": "Command FOO has been disabled"}

to

    {"class": "CommandNotFound", "desc": "The command FOO has not been found"}

This has the additional benefit that the QGA protocol reference
now documents what conditions enable use of the command.

Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
Reviewed-by: Konstantin Kostiuk <kkostiuk@redhat.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Reviewed-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
Message-ID: <20240712132459.3974109-12-berrange@redhat.com>
Signed-off-by: Konstantin Kostiuk <kkostiuk@redhat.com>
2024-07-19 11:50:35 +03:00

182 lines
4.8 KiB
C

/*
* QEMU Guest Agent BSD-specific command implementations
*
* Copyright (c) Virtuozzo International GmbH.
*
* Authors:
* Alexander Ivanov <alexander.ivanov@virtuozzo.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
#include "qemu/osdep.h"
#include "qga-qapi-commands.h"
#include "qapi/qmp/qerror.h"
#include "qapi/error.h"
#include "qemu/queue.h"
#include "commands-common.h"
#include <sys/ioctl.h>
#include <sys/param.h>
#include <sys/ucred.h>
#include <sys/mount.h>
#include <net/if_dl.h>
#if defined(__NetBSD__) || defined(__OpenBSD__)
#include <net/if_arp.h>
#include <netinet/if_ether.h>
#else
#include <net/ethernet.h>
#endif
#include <paths.h>
#if defined(CONFIG_FSFREEZE) || defined(CONFIG_FSTRIM)
bool build_fs_mount_list(FsMountList *mounts, Error **errp)
{
FsMount *mount;
struct statfs *mntbuf, *mntp;
struct stat statbuf;
int i, count, ret;
count = getmntinfo(&mntbuf, MNT_NOWAIT);
if (count == 0) {
error_setg_errno(errp, errno, "getmntinfo failed");
return false;
}
for (i = 0; i < count; i++) {
mntp = &mntbuf[i];
ret = stat(mntp->f_mntonname, &statbuf);
if (ret != 0) {
error_setg_errno(errp, errno, "stat failed on %s",
mntp->f_mntonname);
return false;
}
mount = g_new0(FsMount, 1);
mount->dirname = g_strdup(mntp->f_mntonname);
mount->devtype = g_strdup(mntp->f_fstypename);
mount->devmajor = major(mount->dev);
mount->devminor = minor(mount->dev);
mount->fsid = mntp->f_fsid;
mount->dev = statbuf.st_dev;
QTAILQ_INSERT_TAIL(mounts, mount, next);
}
return true;
}
#endif /* CONFIG_FSFREEZE || CONFIG_FSTRIM */
#if defined(CONFIG_FSFREEZE)
static int ufssuspend_fd = -1;
static int ufssuspend_cnt;
int64_t qmp_guest_fsfreeze_do_freeze_list(bool has_mountpoints,
strList *mountpoints,
FsMountList mounts,
Error **errp)
{
int ret;
strList *list;
struct FsMount *mount;
if (ufssuspend_fd != -1) {
error_setg(errp, "filesystems have already frozen");
return -1;
}
ufssuspend_cnt = 0;
ufssuspend_fd = qemu_open(_PATH_UFSSUSPEND, O_RDWR, errp);
if (ufssuspend_fd == -1) {
return -1;
}
QTAILQ_FOREACH_REVERSE(mount, &mounts, next) {
/*
* To issue fsfreeze in the reverse order of mounts, check if the
* mount is listed in the list here
*/
if (has_mountpoints) {
for (list = mountpoints; list; list = list->next) {
if (g_str_equal(list->value, mount->dirname)) {
break;
}
}
if (!list) {
continue;
}
}
/* Only UFS supports suspend */
if (!g_str_equal(mount->devtype, "ufs")) {
continue;
}
ret = ioctl(ufssuspend_fd, UFSSUSPEND, &mount->fsid);
if (ret == -1) {
/*
* ioctl returns EBUSY for all the FS except the first one
* that was suspended
*/
if (errno == EBUSY) {
continue;
}
error_setg_errno(errp, errno, "failed to freeze %s",
mount->dirname);
goto error;
}
ufssuspend_cnt++;
}
return ufssuspend_cnt;
error:
close(ufssuspend_fd);
ufssuspend_fd = -1;
return -1;
}
/*
* We don't need to call UFSRESUME ioctl because all the frozen FS
* are thawed on /dev/ufssuspend closing.
*/
int qmp_guest_fsfreeze_do_thaw(Error **errp)
{
int ret = ufssuspend_cnt;
ufssuspend_cnt = 0;
if (ufssuspend_fd != -1) {
close(ufssuspend_fd);
ufssuspend_fd = -1;
}
return ret;
}
#endif /* CONFIG_FSFREEZE */
#ifdef HAVE_GETIFADDRS
/*
* Fill "buf" with MAC address by ifaddrs. Pointer buf must point to a
* buffer with ETHER_ADDR_LEN length at least.
*
* Returns false in case of an error, otherwise true. "obtained" arguument
* is true if a MAC address was obtained successful, otherwise false.
*/
bool guest_get_hw_addr(struct ifaddrs *ifa, unsigned char *buf,
bool *obtained, Error **errp)
{
struct sockaddr_dl *sdp;
*obtained = false;
if (ifa->ifa_addr->sa_family != AF_LINK) {
/* We can get HW address only for AF_LINK family. */
g_debug("failed to get MAC address of %s", ifa->ifa_name);
return true;
}
sdp = (struct sockaddr_dl *)ifa->ifa_addr;
memcpy(buf, sdp->sdl_data + sdp->sdl_nlen, ETHER_ADDR_LEN);
*obtained = true;
return true;
}
#endif /* HAVE_GETIFADDRS */