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