xref: /qemu/qga/commands-bsd.c (revision fb72e779)
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