Sunday, May 02, 2021

Re: [doc] using qemu guest agent on OpenBSD kvm/qemu guests

Index: Makefile
===================================================================
RCS file: /cvs/ports/emulators/qemu/Makefile,v
retrieving revision 1.207
diff -u -r1.207 Makefile
--- Makefile 2 May 2021 08:15:19 -0000 1.207
+++ Makefile 2 May 2021 08:28:20 -0000
@@ -13,6 +13,7 @@
CATEGORIES= emulators
MASTER_SITES= https://download.qemu.org/
EXTRACT_SUFX= .tar.xz
+REVISION-ga= 0

HOMEPAGE= https://www.qemu.org/

Index: patches/patch-qga_commands-posix_c
===================================================================
RCS file: patches/patch-qga_commands-posix_c
diff -N patches/patch-qga_commands-posix_c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ patches/patch-qga_commands-posix_c 2 May 2021 08:28:20 -0000
@@ -0,0 +1,793 @@
+$OpenBSD$
+
+adapted from https://github.com/aborche/qemu-guest-agent
+
+adds support for
+"guest-get-fsinfo"
+"guest-get-vcpus"
+"guest-network-get-interfaces"
+and guest shutdown from host.
+
+setting user password isnt supported on OpenBSD
+
+Index: qga/commands-posix.c
+--- qga/commands-posix.c.orig
++++ qga/commands-posix.c
+@@ -28,6 +28,26 @@
+ #include "qemu/cutils.h"
+ #include "commands-common.h"
+
++#ifdef BSD_GUEST_AGENT
++#include <ifaddrs.h>
++#include <arpa/inet.h>
++#include <sys/socket.h>
++#include <sys/sysctl.h>
++#include <net/if.h>
++#include <sys/socket.h>
++#include <ifaddrs.h>
++#include <sys/statvfs.h>
++#include <net/if_dl.h>
++#include <net/if_types.h>
++#include <netinet/if_ether.h>
++#include <net/if_var.h>
++
++#ifdef __OpenBSD__
++#define SIOCGIFLLADDR _IOWR('i', 32, struct ifreq) /* get link level addr */
++#define SIOCGHWADDR SIOCGIFLLADDR
++#endif
++#endif
++
+ #ifdef HAVE_UTMPX
+ #include <utmpx.h>
+ #endif
+@@ -85,14 +105,23 @@ void qmp_guest_shutdown(bool has_mode, const char *mod
+ {
+ const char *shutdown_flag;
+ Error *local_err = NULL;
++ char *shutdown_mode;
+ pid_t pid;
+ int status;
+
+ slog("guest-shutdown called, mode: %s", mode);
+ if (!has_mode || strcmp(mode, "powerdown") == 0) {
++#ifdef BSD_GUEST_AGENT
++ shutdown_flag = "-p";
++#else
+ shutdown_flag = "-P";
++#endif
+ } else if (strcmp(mode, "halt") == 0) {
++#ifdef BSD_GUEST_AGENT
++ shutdown_flag = "-h";
++#else
+ shutdown_flag = "-H";
++#endif
+ } else if (strcmp(mode, "reboot") == 0) {
+ shutdown_flag = "-r";
+ } else {
+@@ -109,8 +138,15 @@ void qmp_guest_shutdown(bool has_mode, const char *mod
+ reopen_fd_to_null(1);
+ reopen_fd_to_null(2);
+
++ shutdown_mode = g_strdup_printf("hypervisor initiated %s", mode);
++
++#ifdef BSD_GUEST_AGENT
++ execle("/sbin/shutdown", "-h", shutdown_flag, "+0",
++ shutdown_mode, (char*)NULL, environ);
++#else
+ execle("/sbin/shutdown", "shutdown", "-h", shutdown_flag, "+0",
+- "hypervisor initiated shutdown", (char *)NULL, environ);
++ shutdown_mode, (char *)NULL, environ);
++#endif
+ _exit(EXIT_FAILURE);
+ } else if (pid < 0) {
+ error_setg_errno(errp, errno, "failed to create child process");
+@@ -153,9 +189,11 @@ int64_t qmp_guest_get_time(Error **errp)
+ void qmp_guest_set_time(bool has_time, int64_t time_ns, Error **errp)
+ {
+ int ret;
++#ifndef BSD_GUEST_AGENT
+ int status;
+ pid_t pid;
+ Error *local_err = NULL;
++#endif
+ struct timeval tv;
+ static const char hwclock_path[] = "/sbin/hwclock";
+ static int hwclock_available = -1;
+@@ -194,6 +232,7 @@ void qmp_guest_set_time(bool has_time, int64_t time_ns
+ }
+ }
+
++#ifndef BSD_GUEST_AGENT
+ /* Now, if user has passed a time to set and the system time is set, we
+ * just need to synchronize the hardware clock. However, if no time was
+ * passed, user is requesting the opposite: set the system time from the
+@@ -230,6 +269,7 @@ void qmp_guest_set_time(bool has_time, int64_t time_ns
+ error_setg(errp, "hwclock failed to set hardware clock to system time");
+ return;
+ }
++#endif
+ }
+
+ typedef enum {
+@@ -789,7 +829,7 @@ static void build_fs_mount_list(FsMountList *mounts, E
+
+ QTAILQ_INSERT_TAIL(mounts, mount, next);
+ }
+- free(line);
++ g_free(line);
+
+ fclose(fp);
+ }
+@@ -2940,24 +2980,255 @@ void qmp_guest_suspend_hybrid(Error **errp)
+ error_setg(errp, QERR_UNSUPPORTED);
+ }
+
++#ifndef BSD_GUEST_AGENT
+ GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp)
+ {
+ error_setg(errp, QERR_UNSUPPORTED);
+ return NULL;
+ }
++#else
++static GuestNetworkInterfaceList *
++guest_find_interface(GuestNetworkInterfaceList *head,
++ const char *name)
++{
++ for (; head; head = head->next) {
++ if (strcmp(head->value->name, name) == 0) {
++ break;
++ }
++ }
++ return head;
++}
+
++static int guest_get_network_stats(const char *name,
++ GuestNetworkInterfaceStat *stats)
++{
++ return -1;
++}
++
++GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp)
++{
++ GuestNetworkInterfaceList *head = NULL, *cur_item = NULL;
++ struct ifaddrs *ifap, *ifa;
++
++ if (getifaddrs(&ifap) < 0) {
++ error_setg_errno(errp, errno, "getifaddrs failed");
++ goto error;
++ }
++
++ for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
++ GuestNetworkInterfaceList *info;
++ GuestIpAddressList **address_list = NULL, *address_item = NULL;
++ GuestNetworkInterfaceStat *interface_stat = NULL;
++ char addr4[INET_ADDRSTRLEN];
++ char addr6[INET6_ADDRSTRLEN];
++ int sock;
++ struct ifreq ifr;
++ // struct ifnet ifp;
++ // struct if_data ifd;
++ // unsigned char *mac_addr;
++ char mac_addr[18]="00:00:00:00:00:00"; // = char[18];
++ void *p;
++
++ g_debug("Processing %s interface", ifa->ifa_name);
++
++ info = guest_find_interface(head, ifa->ifa_name);
++
++ if (!info) {
++ info = g_malloc0(sizeof(*info));
++ info->value = g_malloc0(sizeof(*info->value));
++ info->value->name = g_strdup(ifa->ifa_name);
++
++ if (!cur_item) {
++ head = cur_item = info;
++ } else {
++ cur_item->next = info;
++ cur_item = info;
++ }
++ }
++
++ if (!info->value->has_hardware_address &&
++ ifa->ifa_flags & SIOCGHWADDR) {
++ /* we haven't obtained HW address yet */
++ sock = socket(PF_INET, SOCK_STREAM, 0);
++ if (sock == -1) {
++ error_setg_errno(errp, errno, "failed to create socket");
++ goto error;
++ }
++ close(sock);
++
++ memset(&ifr, 0, sizeof(ifr));
++ pstrcpy(ifr.ifr_name, IF_NAMESIZE, info->value->name);
++ // memset(&ifp, 0, sizeof(ifp));
++ // memset(&ifd, 0, sizeof(ifd));
++
++ if (ifa->ifa_addr->sa_family == AF_LINK) {
++ struct sockaddr_dl *sdl =
++ (struct sockaddr_dl *)ifa->ifa_addr;
++
++ info->value->has_hardware_address = false;
++
++ if (sdl->sdl_type == IFT_ETHER &&
++ sdl->sdl_alen == ETHER_ADDR_LEN) {
++ snprintf(mac_addr, 18, "%s", ether_ntoa((struct ether_addr *)LLADDR(sdl)));
++ info->value->has_hardware_address = true;
++ }
++ else
++ {
++ info->value->has_hardware_address = true;
++ }
++ info->value->hardware_address = g_strdup_printf("%s", mac_addr);
++ }
++
++ }
++
++ if (ifa->ifa_addr &&
++ ifa->ifa_addr->sa_family == AF_INET) {
++ /* interface with IPv4 address */
++ p = &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
++ if (!inet_ntop(AF_INET, p, addr4, sizeof(addr4))) {
++ error_setg_errno(errp, errno, "inet_ntop failed");
++ goto error;
++ }
++
++ address_item = g_malloc0(sizeof(*address_item));
++ address_item->value = g_malloc0(sizeof(*address_item->value));
++ address_item->value->ip_address = g_strdup(addr4);
++ address_item->value->ip_address_type = GUEST_IP_ADDRESS_TYPE_IPV4;
++
++ if (ifa->ifa_netmask) {
++ /* Count the number of set bits in netmask.
++ * This is safe as '1' and '0' cannot be shuffled in netmask. */
++ p = &((struct sockaddr_in *)ifa->ifa_netmask)->sin_addr;
++ address_item->value->prefix = ctpop32(((uint32_t *) p)[0]);
++ }
++ } else if (ifa->ifa_addr &&
++ ifa->ifa_addr->sa_family == AF_INET6) {
++ /* interface with IPv6 address */
++ p = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
++ if (!inet_ntop(AF_INET6, p, addr6, sizeof(addr6))) {
++ error_setg_errno(errp, errno, "inet_ntop failed");
++ goto error;
++ }
++
++ address_item = g_malloc0(sizeof(*address_item));
++ address_item->value = g_malloc0(sizeof(*address_item->value));
++ address_item->value->ip_address = g_strdup(addr6);
++ address_item->value->ip_address_type = GUEST_IP_ADDRESS_TYPE_IPV6;
++
++ if (ifa->ifa_netmask) {
++ /* Count the number of set bits in netmask.
++ * This is safe as '1' and '0' cannot be shuffled in netmask. */
++ p = &((struct sockaddr_in6 *)ifa->ifa_netmask)->sin6_addr;
++ address_item->value->prefix =
++ ctpop32(((uint32_t *) p)[0]) +
++ ctpop32(((uint32_t *) p)[1]) +
++ ctpop32(((uint32_t *) p)[2]) +
++ ctpop32(((uint32_t *) p)[3]);
++ }
++ }
++
++ if (!address_item) {
++ continue;
++ }
++
++ address_list = &info->value->ip_addresses;
++
++ while (*address_list && (*address_list)->next) {
++ address_list = &(*address_list)->next;
++ }
++
++ if (!*address_list) {
++ *address_list = address_item;
++ } else {
++ (*address_list)->next = address_item;
++ }
++
++ info->value->has_ip_addresses = true;
++
++ if (!info->value->has_statistics) {
++ interface_stat = g_malloc0(sizeof(*interface_stat));
++ if (guest_get_network_stats(info->value->name,
++ interface_stat) == -1) {
++ info->value->has_statistics = false;
++ g_free(interface_stat);
++ } else {
++ info->value->statistics = interface_stat;
++ info->value->has_statistics = true;
++ }
++ }
++ }
++
++ freeifaddrs(ifap);
++ return head;
++
++error:
++ freeifaddrs(ifap);
++ qapi_free_GuestNetworkInterfaceList(head);
++ return NULL;
++}
++#endif // BSD_GUEST_AGENT
++
++#ifndef BSD_GUEST_AGENT
+ GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp)
+ {
+ error_setg(errp, QERR_UNSUPPORTED);
+ return NULL;
+ }
++#else
++GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp)
++{
++ int64_t current;
++ GuestLogicalProcessorList *head, **link;
++ long sc_max;
++ Error *local_err = NULL;
++ int Query[2];
++ int NumCpu = 0;
++ size_t Length = sizeof(NumCpu);
++ Query[0] = CTL_HW;
++ Query[1] = HW_NCPU;
+
++ current = 0;
++ head = NULL;
++ link = &head;
++ if (sysctl(Query, 2, &NumCpu, &Length, NULL, 0) == -1) {
++ error_setg(errp, "sysctl get CTL_HW.HW_NCPU failed");
++ }
++ sc_max = NumCpu;
++
++ while (local_err == NULL && current < sc_max) {
++ GuestLogicalProcessor *vcpu;
++ GuestLogicalProcessorList *entry;
++ int64_t id = current++;
++ vcpu = g_malloc0(sizeof *vcpu);
++ vcpu->logical_id = id;
++ vcpu->has_can_offline = false; /* lolspeak ftw */
++ vcpu->online = true;
++ vcpu->can_offline = false;
++ entry = g_malloc0(sizeof *entry);
++ entry->value = vcpu;
++ *link = entry;
++ link = &entry->next;
++ }
++
++ if (local_err == NULL) {
++ /* there's no guest with zero VCPUs */
++ g_assert(head != NULL);
++ return head;
++ }
++
++ qapi_free_GuestLogicalProcessorList(head);
++ error_propagate(errp, local_err);
++ return NULL;
++}
++#endif // BSD_GUEST_AGENT
++
+ int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp)
+ {
+ error_setg(errp, QERR_UNSUPPORTED);
+ return -1;
+ }
+
++#ifndef BSD_GUEST_AGENT
+ void qmp_guest_set_user_password(const char *username,
+ const char *password,
+ bool crypted,
+@@ -2966,6 +3237,114 @@ void qmp_guest_set_user_password(const char *username,
+ error_setg(errp, QERR_UNSUPPORTED);
+ }
+
++#else
++void qmp_guest_set_user_password(const char *username,
++ const char *password,
++ bool crypted,
++ Error **errp)
++{
++ Error *local_err = NULL;
++ char *pw_path = NULL;
++ pid_t pid;
++ int status;
++ int datafd[2] = { -1, -1 };
++ char *rawpasswddata = NULL;
++ size_t rawpasswdlen;
++ char *chpasswddata = NULL;
++ size_t chpasswdlen;
++
++ rawpasswddata = (char *)qbase64_decode(password, -1, &rawpasswdlen, errp);
++ if (!rawpasswddata) {
++ return;
++ }
++
++ rawpasswddata = g_renew(char, rawpasswddata, rawpasswdlen + 1);
++ rawpasswddata[rawpasswdlen] = '\0';
++
++ if (strchr(rawpasswddata, '\n')) {
++ error_setg(errp, "forbidden characters in raw password");
++ goto out;
++ }
++
++ if (strchr(username, '\n') ||
++ strchr(username, ':')) {
++ error_setg(errp, "forbidden characters in username");
++ goto out;
++ }
++
++ chpasswddata = g_strdup_printf("%s", rawpasswddata);
++ chpasswdlen = strlen(chpasswddata);
++
++ pw_path = g_find_program_in_path("pw");
++
++ if (!pw_path) {
++ error_setg(errp, "cannot find 'pw' program in PATH");
++ goto out;
++ }
++
++ if (pipe(datafd) < 0) {
++ error_setg(errp, "cannot create pipe FDs");
++ goto out;
++ }
++
++ pid = fork();
++ if (pid == 0) {
++ close(datafd[1]);
++ /* child */
++ setsid();
++ dup2(datafd[0], 0);
++ reopen_fd_to_null(1);
++ reopen_fd_to_null(2);
++
++ if (crypted) {
++ execle(pw_path, "pw", "usermod", username, "-H", "0", (char*)NULL, environ);
++ } else {
++ execle(pw_path, "pw", "usermod", username, "-h", "0", (char*)NULL, environ);
++ }
++ _exit(EXIT_FAILURE);
++ } else if (pid < 0) {
++ error_setg_errno(errp, errno, "failed to create child process");
++ goto out;
++ }
++ close(datafd[0]);
++ datafd[0] = -1;
++
++ if (qemu_write_full(datafd[1], chpasswddata, chpasswdlen) != chpasswdlen) {
++ error_setg_errno(errp, errno, "cannot write new account password");
++ goto out;
++ }
++ close(datafd[1]);
++ datafd[1] = -1;
++
++ ga_wait_child(pid, &status, &local_err);
++ if (local_err) {
++ error_propagate(errp, local_err);
++ goto out;
++ }
++
++ if (!WIFEXITED(status)) {
++ error_setg(errp, "child process has terminated abnormally");
++ goto out;
++ }
++
++ if (WEXITSTATUS(status)) {
++ error_setg(errp, "child process has failed to set user password");
++ goto out;
++ }
++
++out:
++ g_free(chpasswddata);
++ g_free(rawpasswddata);
++ g_free(pw_path);
++ if (datafd[0] != -1) {
++ close(datafd[0]);
++ }
++ if (datafd[1] != -1) {
++ close(datafd[1]);
++ }
++}
++#endif // BSD_GUEST_AGENT
++
+ GuestMemoryBlockList *qmp_guest_get_memory_blocks(Error **errp)
+ {
+ error_setg(errp, QERR_UNSUPPORTED);
+@@ -2989,12 +3368,258 @@ GuestMemoryBlockInfo *qmp_guest_get_memory_block_info(
+
+ #if !defined(CONFIG_FSFREEZE)
+
++#ifndef BSD_GUEST_AGENT
+ GuestFilesystemInfoList *qmp_guest_get_fsinfo(Error **errp)
+ {
+ error_setg(errp, QERR_UNSUPPORTED);
+ return NULL;
+ }
+
++#else
++/* ===================================================================== */
++typedef struct FsMount {
++ char *dirname;
++ char *devtype;
++ char *size;
++ char *used;
++ char *free;
++ char *load;
++ char *mntpoint;
++ unsigned int devmajor, devminor;
++ QTAILQ_ENTRY(FsMount) next;
++} FsMount;
++
++typedef QTAILQ_HEAD(FsMountList, FsMount) FsMountList;
++
++static void free_fs_mount_list(FsMountList *mounts)
++{
++ FsMount *mount, *temp;
++
++ if (!mounts) {
++ return;
++ }
++
++ QTAILQ_FOREACH_SAFE(mount, mounts, next, temp) {
++ QTAILQ_REMOVE(mounts, mount, next);
++ g_free(mount->dirname);
++ g_free(mount->devtype);
++ g_free(mount->size);
++ g_free(mount->used);
++ g_free(mount->free);
++ g_free(mount->load);
++ g_free(mount->mntpoint);
++ g_free(mount);
++ }
++}
++
++
++//#define BUFSIZE 2048
++
++static void build_fs_mount_list(FsMountList *mounts, Error **errp)
++{
++ FsMount *mount;
++#ifdef __OpenBSD__
++ char const *dfcmd = "/bin/df";
++#else // defined(__OpenBSD__)
++ char const *dfcmd = "/bin/df -hT";
++#endif // defined(__OpenBSD__)
++ //char buf[BUFSIZE];
++
++ FILE *fp;
++ char *line = NULL;
++ //, *dash;
++ size_t n;
++ int ret;
++ //, dir_s, dir_e, type_s, type_e, dev_s, dev_e;
++ char dev_name[128], size[12], used[12], free[12], load[10], mounted[128];
++#ifndef __OpenBSD__
++ char fstype[12] = "";
++#endif // !defined(__OpenBSD__)
++ //int dev_name, fstype, size, used, free, load, mounted;
++
++ if ((fp = popen(dfcmd, "r")) == NULL) {
++ g_debug("Cannot open '%s'!!\n", dfcmd);
++ error_setg_errno(errp, errno,
++ "failed to create child process for command: %s",
++ dfcmd);
++ return;
++ }
++
++ while (getline(&line, &n, fp) != -1) {
++ //g_debug("line '%s'", line);
++#ifdef __OpenBSD__
++ ret = sscanf(line, "%127s%11s%11s%11s%9s%127s",
++ dev_name, size, used, free, load, mounted);
++#else // defined(__OpenBSD__)
++ ret = sscanf(line, "%127s%11s%11s%11s%11s%9s%127s",
++ dev_name, fstype, size, used, free, load, mounted);
++ //g_debug("ret %d, dev_name '%s', fstype '%s', size '%s', used '%s', free '%s', load '%s', mounted '%s'",
++ // ret, dev_name, fstype, size, used, free, load, mounted);
++#endif // defined(__OpenBSD__)
++ if (g_str_equal(dev_name, "Filesystem")
++#ifndef __OpenBSD__
++ ||g_str_equal(fstype,"devfs")
++ ||g_str_equal(fstype,"procfs")
++ ||g_str_equal(fstype,"fdescfs")
++#endif // !defined(__OpenBSD__)
++ ) {
++ continue;
++ }
++
++#ifdef __OpenBSD__
++ if (ret < 6) {
++#else // defined(__OpenBSD__)
++ if (ret < 7) {
++#endif // defined(__OpenBSD__)
++ continue;
++ }
++
++ mount = g_new0(FsMount, 1);
++ mount->dirname = g_strdup(dev_name);
++#ifndef __OpenBSD__
++ mount->devtype = g_strdup(fstype);
++#endif // defined(__OpenBSD__)
++ mount->free = g_strdup(free);
++ mount->load = g_strdup(load);
++ mount->size = g_strdup(size);
++ mount->used = g_strdup(used);
++ mount->mntpoint = g_strdup(mounted);
++ mount->devmajor = 0;
++ mount->devminor = 0;
++
++ QTAILQ_INSERT_TAIL(mounts, mount, next);
++ }
++ g_free(line);
++
++ fclose(fp);
++}
++
++#ifdef __OpenBSD__
++
++static void add_type_fs_mount_list(FsMountList *mounts, Error **errp)
++{
++ FILE *fp;
++ char const *mountcmd = "/sbin/mount";
++ char *line = NULL;
++ size_t n;
++ int ret;
++ char mnt_fsname[128], mnt_dir[128], mnt_type[32], mnt_opts[128];
++ struct FsMount *mount;
++
++ // get mounts from mount command
++ if ((fp = popen(mountcmd, "r")) == NULL) {
++ g_debug("Cannot open '%s'!!\n", mountcmd);
++ error_setg_errno(errp, errno, "failed to create child process for command: %s", mountcmd);
++ return;
++ }
++
++ // loop through mounts from mount command
++ while (getline(&line, &n, fp) != -1) {
++ //g_debug("line '%s'", line);
++
++ ret = sscanf(line, "%127s on %127s type %31s (%127s)",
++ mnt_fsname, mnt_dir, mnt_type, mnt_opts);
++ //g_debug("ret %d, fsname '%s', dir '%s', type '%s', opts '%s'",
++ // ret, mnt_fsname, mnt_dir, mnt_type, mnt_opts);
++
++ if (4 != ret ||
++ '/' != mnt_fsname[0] ||
++ '/' != mnt_dir[0] ||
++ g_str_equal("smbfs", mnt_type) ||
++ g_str_equal("cifs", mnt_type)) {
++ continue;
++ }
++
++ // find mount in supplied mounts list and update device type
++ QTAILQ_FOREACH(mount, mounts, next) {
++ if (NULL == mount->devtype && g_str_equal(mount->dirname, mnt_fsname)) {
++ mount->devtype = g_strdup(mnt_type);
++ break;
++ }
++ }
++ }
++ g_free(line);
++
++ fclose(fp);
++}
++
++#endif // defined(__OpenBSD__)
++
++/* ======================================================= */
++
++
++/* Return a list of the disk device(s)' info which @mount lies on */
++static GuestFilesystemInfo *build_guest_fsinfo(struct FsMount *mount,
++ Error **errp)
++{
++ GuestFilesystemInfo *fs = g_malloc0(sizeof(*fs));
++ struct statvfs buf;
++ unsigned long used, nonroot_total, fr_size;
++
++ fs->name = g_strdup(mount->dirname);
++ fs->mountpoint = g_strdup(mount->mntpoint);
++ fs->type = g_strdup(mount->devtype);
++
++ if (statvfs(fs->mountpoint, &buf) == 0) {
++ fr_size = buf.f_frsize;
++ used = buf.f_blocks - buf.f_bfree;
++ nonroot_total = used + buf.f_bavail;
++ fs->used_bytes = used * fr_size;
++ fs->total_bytes = nonroot_total * fr_size;
++
++ fs->has_total_bytes = true;
++ fs->has_used_bytes = true;
++ }
++
++ //g_free(devpath);
++
++ return fs;
++}
++
++GuestFilesystemInfoList *qmp_guest_get_fsinfo(Error **errp)
++{
++ FsMountList mounts;
++ struct FsMount *mount;
++ GuestFilesystemInfoList *new, *ret = NULL;
++ Error *local_err = NULL;
++
++ QTAILQ_INIT(&mounts);
++
++ g_debug("Entering to guest_get_fsinfo");
++ build_fs_mount_list(&mounts, &local_err);
++ if (local_err) {
++ error_propagate(errp, local_err);
++ return NULL;
++ }
++
++#ifdef __OpenBSD__
++ add_type_fs_mount_list(&mounts, &local_err);
++ if (local_err) {
++ error_propagate(errp, local_err);
++ return NULL;
++ }
++#endif // defined(__OpenBSD__)
++
++ QTAILQ_FOREACH(mount, &mounts, next) {
++ //g_debug("Building guest fsinfo for '%s'", mount->dirname);
++ //g_debug("Devtype '%s'", mount->devtype);
++ new = g_malloc0(sizeof(*ret));
++ new->value = build_guest_fsinfo(mount, &local_err);
++ new->next = ret;
++ ret = new;
++ if (local_err) {
++ error_propagate(errp, local_err);
++ qapi_free_GuestFilesystemInfoList(ret);
++ ret = NULL;
++ break;
++ }
++ }
++
++ free_fs_mount_list(&mounts);
++ return ret;
++}
++#endif // BSD_GUEST_AGENT
++
+ GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **errp)
+ {
+ error_setg(errp, QERR_UNSUPPORTED);
+@@ -3047,6 +3672,7 @@ GList *ga_command_blacklist_init(GList *blacklist)
+ {
+ #if !defined(__linux__)
+ {
++#ifndef BSD_GUEST_AGENT
+ const char *list[] = {
+ "guest-suspend-disk", "guest-suspend-ram",
+ "guest-suspend-hybrid", "guest-network-get-interfaces",
+@@ -3054,6 +3680,15 @@ GList *ga_command_blacklist_init(GList *blacklist)
+ "guest-get-memory-blocks", "guest-set-memory-blocks",
+ "guest-get-memory-block-size", "guest-get-memory-block-info",
+ NULL};
++#else
++ const char *list[] = {
++ "guest-suspend-disk", "guest-suspend-ram",
++ "guest-suspend-hybrid",
++ "guest-set-vcpus",
++ "guest-get-memory-blocks", "guest-set-memory-blocks",
++ "guest-get-memory-block-size", "guest-get-memory-block-info",
++ NULL};
++#endif // BSD_GUEST_AGENT
+ char **p = (char **)list;
+
+ while (*p) {
+@@ -3064,11 +3699,18 @@ GList *ga_command_blacklist_init(GList *blacklist)
+
+ #if !defined(CONFIG_FSFREEZE)
+ {
++#ifndef BSD_GUEST_AGENT
+ const char *list[] = {
+ "guest-get-fsinfo", "guest-fsfreeze-status",
+ "guest-fsfreeze-freeze", "guest-fsfreeze-freeze-list",
+ "guest-fsfreeze-thaw", "guest-get-fsinfo",
+ "guest-get-disks", NULL};
++#else
++ const char *list[] = {
++ "guest-fsfreeze-status",
++ "guest-fsfreeze-freeze", "guest-fsfreeze-freeze-list",
++ "guest-fsfreeze-thaw", NULL};
++#endif // BSD_GUEST_AGENT
+ char **p = (char **)list;
+
+ while (*p) {
Index: patches/patch-qga_main_c
===================================================================
RCS file: patches/patch-qga_main_c
diff -N patches/patch-qga_main_c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ patches/patch-qga_main_c 2 May 2021 08:28:20 -0000
@@ -0,0 +1,35 @@
+$OpenBSD$
+
+adapted from https://github.com/aborche/qemu-guest-agent
+
+Index: qga/main.c
+--- qga/main.c.orig
++++ qga/main.c
+@@ -46,9 +46,15 @@
+ #endif
+
+ #ifndef _WIN32
++#ifdef BSD_GUEST_AGENT
++#define QGA_VIRTIO_PATH_DEFAULT "/dev/vtcon/org.qemu.guest_agent.0"
++#define QGA_STATE_RELATIVE_DIR ""
++#define QGA_SERIAL_PATH_DEFAULT "/dev/cua01"
++#else
+ #define QGA_VIRTIO_PATH_DEFAULT "/dev/virtio-ports/org.qemu.guest_agent.0"
+ #define QGA_STATE_RELATIVE_DIR "run"
+ #define QGA_SERIAL_PATH_DEFAULT "/dev/ttyS0"
++#endif
+ #else
+ #define QGA_VIRTIO_PATH_DEFAULT "\\\\.\\Global\\org.qemu.guest_agent.0"
+ #define QGA_STATE_RELATIVE_DIR "qemu-ga"
+@@ -1471,7 +1477,11 @@ int main(int argc, char **argv)
+ }
+
+ if (config->method == NULL) {
++#ifdef BSD_GUEST_AGENT
++ config->method = g_strdup("isa-serial");
++#else
+ config->method = g_strdup("virtio-serial");
++#endif
+ }
+
+ socket_activation = check_socket_activation();
Le Sat, Apr 10, 2021 at 07:29:06PM -0400, Brad Smith a écrit :
> On 4/10/2021 3:05 AM, Landry Breuil wrote:
> > On Fri, Apr 09, 2021 at 04:53:17PM +0200, Landry Breuil wrote:
> > > On Thu, Apr 08, 2021 at 06:19:58PM +0200, Jeroen Derks wrote:
> > > > On 08/04/2021 14:35, Landry Breuil wrote:>
> > > > > well that's very nice, definitely interested in those freebsd patches !
> > > > > not 100% sure those will get in 6.9 though as we're slowly moving towards
> > > > > lock, but send them anyway, and we'll figure out what we can do from
> > > > > there. I suppose those come from https://github.com/aborche/qemu-guest-agent ?
> > > > Indeed, here they are (if there's interest I can also send a diff of the patches
> > > > compared to the FreeBSD patches to see what I've done):
> > > oh, well that looks nice. A bit rought around the edges imo, and im not
> > > sure we want to maintain a rather large patch like this in the
> > > portstree. according to https://wiki.qemu.org/Contribute/SubmitAPatch i
> > > think it should be pushed upstream, do you have any idea if the original
> > > freebsd patches were proposed there or qemu-devel list ?
> > >
> > > i'll let brad decide, but as is this doesnt apply as-is against 6.0 rcs
> > > which we're testing for post-6.9....
> > so i've backported your patch on top of brad's 6.0rc2 qemu update, and
> > fixed things here and there, removed commented code, and yeah get-vcpus,
> > get-fsinfo and network-get-interfaces seems to work fine !
>
> Thanks.
>
> I don't mind keeping this or some variation on it around. It is small
> enough.

here's a new patch below, with the patches massaged for qemu 6.0.

> I would very much hope that there is an attempt to upstream this in some
> form.

i had filed https://github.com/aborche/qemu-guest-agent/issues/18 in the
freebsd patches repo, guess at that point one just needs to polish the
patches against qemu git master and send patches to qemu-devel..

i might get there at some point :)

Landry

No comments:

Post a Comment