/* * QEMU Guest Agent BSD-specific command implementations * * Copyright (c) Virtuozzo International GmbH. * * Authors: * Alexander Ivanov * * 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/error.h" #include "qemu/queue.h" #include "commands-common.h" #include #include #include #include #include #if defined(__NetBSD__) || defined(__OpenBSD__) #include #include #else #include #endif #include #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 */