1 /* 2 * QEMU Guest Agent BSD-specific command implementations 3 * 4 * Copyright (c) Virtuozzo International GmbH. 5 * 6 * Authors: 7 * Alexander Ivanov <alexander.ivanov@virtuozzo.com> 8 * 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. 11 */ 12 13 #include "qemu/osdep.h" 14 #include "qga-qapi-commands.h" 15 #include "qapi/qmp/qerror.h" 16 #include "qapi/error.h" 17 #include "qemu/queue.h" 18 #include "commands-common.h" 19 #include <sys/ioctl.h> 20 #include <sys/param.h> 21 #include <sys/ucred.h> 22 #include <sys/mount.h> 23 #include <net/if_dl.h> 24 #include <net/ethernet.h> 25 #include <paths.h> 26 27 #if defined(CONFIG_FSFREEZE) || defined(CONFIG_FSTRIM) 28 bool build_fs_mount_list(FsMountList *mounts, Error **errp) 29 { 30 FsMount *mount; 31 struct statfs *mntbuf, *mntp; 32 struct stat statbuf; 33 int i, count, ret; 34 35 count = getmntinfo(&mntbuf, MNT_NOWAIT); 36 if (count == 0) { 37 error_setg_errno(errp, errno, "getmntinfo failed"); 38 return false; 39 } 40 41 for (i = 0; i < count; i++) { 42 mntp = &mntbuf[i]; 43 ret = stat(mntp->f_mntonname, &statbuf); 44 if (ret != 0) { 45 error_setg_errno(errp, errno, "stat failed on %s", 46 mntp->f_mntonname); 47 return false; 48 } 49 50 mount = g_new0(FsMount, 1); 51 52 mount->dirname = g_strdup(mntp->f_mntonname); 53 mount->devtype = g_strdup(mntp->f_fstypename); 54 mount->devmajor = major(mount->dev); 55 mount->devminor = minor(mount->dev); 56 mount->fsid = mntp->f_fsid; 57 mount->dev = statbuf.st_dev; 58 59 QTAILQ_INSERT_TAIL(mounts, mount, next); 60 } 61 return true; 62 } 63 #endif /* CONFIG_FSFREEZE || CONFIG_FSTRIM */ 64 65 #if defined(CONFIG_FSFREEZE) 66 static int ufssuspend_fd = -1; 67 static int ufssuspend_cnt; 68 69 int64_t qmp_guest_fsfreeze_do_freeze_list(bool has_mountpoints, 70 strList *mountpoints, 71 FsMountList mounts, 72 Error **errp) 73 { 74 int ret; 75 strList *list; 76 struct FsMount *mount; 77 78 if (ufssuspend_fd != -1) { 79 error_setg(errp, "filesystems have already frozen"); 80 return -1; 81 } 82 83 ufssuspend_cnt = 0; 84 ufssuspend_fd = qemu_open(_PATH_UFSSUSPEND, O_RDWR, errp); 85 if (ufssuspend_fd == -1) { 86 return -1; 87 } 88 89 QTAILQ_FOREACH_REVERSE(mount, &mounts, next) { 90 /* 91 * To issue fsfreeze in the reverse order of mounts, check if the 92 * mount is listed in the list here 93 */ 94 if (has_mountpoints) { 95 for (list = mountpoints; list; list = list->next) { 96 if (g_str_equal(list->value, mount->dirname)) { 97 break; 98 } 99 } 100 if (!list) { 101 continue; 102 } 103 } 104 105 /* Only UFS supports suspend */ 106 if (!g_str_equal(mount->devtype, "ufs")) { 107 continue; 108 } 109 110 ret = ioctl(ufssuspend_fd, UFSSUSPEND, &mount->fsid); 111 if (ret == -1) { 112 /* 113 * ioctl returns EBUSY for all the FS except the first one 114 * that was suspended 115 */ 116 if (errno == EBUSY) { 117 continue; 118 } 119 error_setg_errno(errp, errno, "failed to freeze %s", 120 mount->dirname); 121 goto error; 122 } 123 ufssuspend_cnt++; 124 } 125 return ufssuspend_cnt; 126 error: 127 close(ufssuspend_fd); 128 ufssuspend_fd = -1; 129 return -1; 130 131 } 132 133 /* 134 * We don't need to call UFSRESUME ioctl because all the frozen FS 135 * are thawed on /dev/ufssuspend closing. 136 */ 137 int qmp_guest_fsfreeze_do_thaw(Error **errp) 138 { 139 int ret = ufssuspend_cnt; 140 ufssuspend_cnt = 0; 141 if (ufssuspend_fd != -1) { 142 close(ufssuspend_fd); 143 ufssuspend_fd = -1; 144 } 145 return ret; 146 } 147 148 GuestFilesystemInfoList *qmp_guest_get_fsinfo(Error **errp) 149 { 150 error_setg(errp, QERR_UNSUPPORTED); 151 return NULL; 152 } 153 154 GuestDiskInfoList *qmp_guest_get_disks(Error **errp) 155 { 156 error_setg(errp, QERR_UNSUPPORTED); 157 return NULL; 158 } 159 160 GuestDiskStatsInfoList *qmp_guest_get_diskstats(Error **errp) 161 { 162 error_setg(errp, QERR_UNSUPPORTED); 163 return NULL; 164 } 165 166 GuestCpuStatsList *qmp_guest_get_cpustats(Error **errp) 167 { 168 error_setg(errp, QERR_UNSUPPORTED); 169 return NULL; 170 } 171 #endif /* CONFIG_FSFREEZE */ 172 173 #ifdef HAVE_GETIFADDRS 174 /* 175 * Fill "buf" with MAC address by ifaddrs. Pointer buf must point to a 176 * buffer with ETHER_ADDR_LEN length at least. 177 * 178 * Returns false in case of an error, otherwise true. "obtained" arguument 179 * is true if a MAC address was obtained successful, otherwise false. 180 */ 181 bool guest_get_hw_addr(struct ifaddrs *ifa, unsigned char *buf, 182 bool *obtained, Error **errp) 183 { 184 struct sockaddr_dl *sdp; 185 186 *obtained = false; 187 188 if (ifa->ifa_addr->sa_family != AF_LINK) { 189 /* We can get HW address only for AF_LINK family. */ 190 g_debug("failed to get MAC address of %s", ifa->ifa_name); 191 return true; 192 } 193 194 sdp = (struct sockaddr_dl *)ifa->ifa_addr; 195 memcpy(buf, sdp->sdl_data + sdp->sdl_nlen, ETHER_ADDR_LEN); 196 *obtained = true; 197 198 return true; 199 } 200 #endif /* HAVE_GETIFADDRS */ 201