xref: /openbmc/qemu/qga/commands-posix.c (revision 1de7afc9)
1c216e5adSMichael Roth /*
2c216e5adSMichael Roth  * QEMU Guest Agent POSIX-specific command implementations
3c216e5adSMichael Roth  *
4c216e5adSMichael Roth  * Copyright IBM Corp. 2011
5c216e5adSMichael Roth  *
6c216e5adSMichael Roth  * Authors:
7c216e5adSMichael Roth  *  Michael Roth      <mdroth@linux.vnet.ibm.com>
83424fc9fSMichal Privoznik  *  Michal Privoznik  <mprivozn@redhat.com>
9c216e5adSMichael Roth  *
10c216e5adSMichael Roth  * This work is licensed under the terms of the GNU GPL, version 2 or later.
11c216e5adSMichael Roth  * See the COPYING file in the top-level directory.
12c216e5adSMichael Roth  */
13c216e5adSMichael Roth 
14c216e5adSMichael Roth #include <glib.h>
15c216e5adSMichael Roth #include <sys/types.h>
16c216e5adSMichael Roth #include <sys/ioctl.h>
172c02cbf6SLuiz Capitulino #include <sys/wait.h>
18c216e5adSMichael Roth #include "qga/guest-agent-core.h"
19c216e5adSMichael Roth #include "qga-qmp-commands.h"
207b1b5d19SPaolo Bonzini #include "qapi/qmp/qerror.h"
21*1de7afc9SPaolo Bonzini #include "qemu/queue.h"
22*1de7afc9SPaolo Bonzini #include "qemu/host-utils.h"
23c216e5adSMichael Roth 
242c02cbf6SLuiz Capitulino #ifndef CONFIG_HAS_ENVIRON
25eecae147SAndreas Färber #ifdef __APPLE__
26eecae147SAndreas Färber #include <crt_externs.h>
27eecae147SAndreas Färber #define environ (*_NSGetEnviron())
28eecae147SAndreas Färber #else
292c02cbf6SLuiz Capitulino extern char **environ;
302c02cbf6SLuiz Capitulino #endif
31eecae147SAndreas Färber #endif
322c02cbf6SLuiz Capitulino 
33e72c3f2eSMichael Roth #if defined(__linux__)
34e72c3f2eSMichael Roth #include <mntent.h>
35e72c3f2eSMichael Roth #include <linux/fs.h>
36e72c3f2eSMichael Roth #include <ifaddrs.h>
37e72c3f2eSMichael Roth #include <arpa/inet.h>
38e72c3f2eSMichael Roth #include <sys/socket.h>
39e72c3f2eSMichael Roth #include <net/if.h>
40e72c3f2eSMichael Roth 
41eab5fd59SPaolo Bonzini #ifdef FIFREEZE
42e72c3f2eSMichael Roth #define CONFIG_FSFREEZE
43e72c3f2eSMichael Roth #endif
44eab5fd59SPaolo Bonzini #ifdef FITRIM
45eab5fd59SPaolo Bonzini #define CONFIG_FSTRIM
46eab5fd59SPaolo Bonzini #endif
47e72c3f2eSMichael Roth #endif
48e72c3f2eSMichael Roth 
49c216e5adSMichael Roth void qmp_guest_shutdown(bool has_mode, const char *mode, Error **err)
50c216e5adSMichael Roth {
51c216e5adSMichael Roth     const char *shutdown_flag;
52d5dd3498SLuiz Capitulino     pid_t rpid, pid;
533674838cSLuiz Capitulino     int status;
54c216e5adSMichael Roth 
55c216e5adSMichael Roth     slog("guest-shutdown called, mode: %s", mode);
56c216e5adSMichael Roth     if (!has_mode || strcmp(mode, "powerdown") == 0) {
57c216e5adSMichael Roth         shutdown_flag = "-P";
58c216e5adSMichael Roth     } else if (strcmp(mode, "halt") == 0) {
59c216e5adSMichael Roth         shutdown_flag = "-H";
60c216e5adSMichael Roth     } else if (strcmp(mode, "reboot") == 0) {
61c216e5adSMichael Roth         shutdown_flag = "-r";
62c216e5adSMichael Roth     } else {
63c216e5adSMichael Roth         error_set(err, QERR_INVALID_PARAMETER_VALUE, "mode",
64c216e5adSMichael Roth                   "halt|powerdown|reboot");
65c216e5adSMichael Roth         return;
66c216e5adSMichael Roth     }
67c216e5adSMichael Roth 
68d5dd3498SLuiz Capitulino     pid = fork();
69d5dd3498SLuiz Capitulino     if (pid == 0) {
70c216e5adSMichael Roth         /* child, start the shutdown */
71c216e5adSMichael Roth         setsid();
723674838cSLuiz Capitulino         reopen_fd_to_null(0);
733674838cSLuiz Capitulino         reopen_fd_to_null(1);
743674838cSLuiz Capitulino         reopen_fd_to_null(2);
75c216e5adSMichael Roth 
763674838cSLuiz Capitulino         execle("/sbin/shutdown", "shutdown", shutdown_flag, "+0",
773674838cSLuiz Capitulino                "hypervisor initiated shutdown", (char*)NULL, environ);
783674838cSLuiz Capitulino         _exit(EXIT_FAILURE);
79d5dd3498SLuiz Capitulino     } else if (pid < 0) {
80d5dd3498SLuiz Capitulino         goto exit_err;
81c216e5adSMichael Roth     }
82d5dd3498SLuiz Capitulino 
83d5dd3498SLuiz Capitulino     do {
84d5dd3498SLuiz Capitulino         rpid = waitpid(pid, &status, 0);
85d5dd3498SLuiz Capitulino     } while (rpid == -1 && errno == EINTR);
86d5dd3498SLuiz Capitulino     if (rpid == pid && WIFEXITED(status) && !WEXITSTATUS(status)) {
87d5dd3498SLuiz Capitulino         return;
88d5dd3498SLuiz Capitulino     }
89d5dd3498SLuiz Capitulino 
90d5dd3498SLuiz Capitulino exit_err:
91d5dd3498SLuiz Capitulino     error_set(err, QERR_UNDEFINED_ERROR);
92c216e5adSMichael Roth }
93c216e5adSMichael Roth 
94c216e5adSMichael Roth typedef struct GuestFileHandle {
95c216e5adSMichael Roth     uint64_t id;
96c216e5adSMichael Roth     FILE *fh;
97c216e5adSMichael Roth     QTAILQ_ENTRY(GuestFileHandle) next;
98c216e5adSMichael Roth } GuestFileHandle;
99c216e5adSMichael Roth 
100c216e5adSMichael Roth static struct {
101c216e5adSMichael Roth     QTAILQ_HEAD(, GuestFileHandle) filehandles;
102c216e5adSMichael Roth } guest_file_state;
103c216e5adSMichael Roth 
104c216e5adSMichael Roth static void guest_file_handle_add(FILE *fh)
105c216e5adSMichael Roth {
106c216e5adSMichael Roth     GuestFileHandle *gfh;
107c216e5adSMichael Roth 
108c216e5adSMichael Roth     gfh = g_malloc0(sizeof(GuestFileHandle));
109c216e5adSMichael Roth     gfh->id = fileno(fh);
110c216e5adSMichael Roth     gfh->fh = fh;
111c216e5adSMichael Roth     QTAILQ_INSERT_TAIL(&guest_file_state.filehandles, gfh, next);
112c216e5adSMichael Roth }
113c216e5adSMichael Roth 
114c216e5adSMichael Roth static GuestFileHandle *guest_file_handle_find(int64_t id)
115c216e5adSMichael Roth {
116c216e5adSMichael Roth     GuestFileHandle *gfh;
117c216e5adSMichael Roth 
118c216e5adSMichael Roth     QTAILQ_FOREACH(gfh, &guest_file_state.filehandles, next)
119c216e5adSMichael Roth     {
120c216e5adSMichael Roth         if (gfh->id == id) {
121c216e5adSMichael Roth             return gfh;
122c216e5adSMichael Roth         }
123c216e5adSMichael Roth     }
124c216e5adSMichael Roth 
125c216e5adSMichael Roth     return NULL;
126c216e5adSMichael Roth }
127c216e5adSMichael Roth 
128c216e5adSMichael Roth int64_t qmp_guest_file_open(const char *path, bool has_mode, const char *mode, Error **err)
129c216e5adSMichael Roth {
130c216e5adSMichael Roth     FILE *fh;
131c216e5adSMichael Roth     int fd;
132c216e5adSMichael Roth     int64_t ret = -1;
133c216e5adSMichael Roth 
134c216e5adSMichael Roth     if (!has_mode) {
135c216e5adSMichael Roth         mode = "r";
136c216e5adSMichael Roth     }
137c216e5adSMichael Roth     slog("guest-file-open called, filepath: %s, mode: %s", path, mode);
138c216e5adSMichael Roth     fh = fopen(path, mode);
139c216e5adSMichael Roth     if (!fh) {
140c216e5adSMichael Roth         error_set(err, QERR_OPEN_FILE_FAILED, path);
141c216e5adSMichael Roth         return -1;
142c216e5adSMichael Roth     }
143c216e5adSMichael Roth 
144c216e5adSMichael Roth     /* set fd non-blocking to avoid common use cases (like reading from a
145c216e5adSMichael Roth      * named pipe) from hanging the agent
146c216e5adSMichael Roth      */
147c216e5adSMichael Roth     fd = fileno(fh);
148c216e5adSMichael Roth     ret = fcntl(fd, F_GETFL);
149c216e5adSMichael Roth     ret = fcntl(fd, F_SETFL, ret | O_NONBLOCK);
150c216e5adSMichael Roth     if (ret == -1) {
151c216e5adSMichael Roth         error_set(err, QERR_QGA_COMMAND_FAILED, "fcntl() failed");
152c216e5adSMichael Roth         fclose(fh);
153c216e5adSMichael Roth         return -1;
154c216e5adSMichael Roth     }
155c216e5adSMichael Roth 
156c216e5adSMichael Roth     guest_file_handle_add(fh);
157c216e5adSMichael Roth     slog("guest-file-open, handle: %d", fd);
158c216e5adSMichael Roth     return fd;
159c216e5adSMichael Roth }
160c216e5adSMichael Roth 
161c216e5adSMichael Roth void qmp_guest_file_close(int64_t handle, Error **err)
162c216e5adSMichael Roth {
163c216e5adSMichael Roth     GuestFileHandle *gfh = guest_file_handle_find(handle);
164c216e5adSMichael Roth     int ret;
165c216e5adSMichael Roth 
166c216e5adSMichael Roth     slog("guest-file-close called, handle: %ld", handle);
167c216e5adSMichael Roth     if (!gfh) {
168c216e5adSMichael Roth         error_set(err, QERR_FD_NOT_FOUND, "handle");
169c216e5adSMichael Roth         return;
170c216e5adSMichael Roth     }
171c216e5adSMichael Roth 
172c216e5adSMichael Roth     ret = fclose(gfh->fh);
173c216e5adSMichael Roth     if (ret == -1) {
174c216e5adSMichael Roth         error_set(err, QERR_QGA_COMMAND_FAILED, "fclose() failed");
175c216e5adSMichael Roth         return;
176c216e5adSMichael Roth     }
177c216e5adSMichael Roth 
178c216e5adSMichael Roth     QTAILQ_REMOVE(&guest_file_state.filehandles, gfh, next);
179c216e5adSMichael Roth     g_free(gfh);
180c216e5adSMichael Roth }
181c216e5adSMichael Roth 
182c216e5adSMichael Roth struct GuestFileRead *qmp_guest_file_read(int64_t handle, bool has_count,
183c216e5adSMichael Roth                                           int64_t count, Error **err)
184c216e5adSMichael Roth {
185c216e5adSMichael Roth     GuestFileHandle *gfh = guest_file_handle_find(handle);
186c216e5adSMichael Roth     GuestFileRead *read_data = NULL;
187c216e5adSMichael Roth     guchar *buf;
188c216e5adSMichael Roth     FILE *fh;
189c216e5adSMichael Roth     size_t read_count;
190c216e5adSMichael Roth 
191c216e5adSMichael Roth     if (!gfh) {
192c216e5adSMichael Roth         error_set(err, QERR_FD_NOT_FOUND, "handle");
193c216e5adSMichael Roth         return NULL;
194c216e5adSMichael Roth     }
195c216e5adSMichael Roth 
196c216e5adSMichael Roth     if (!has_count) {
197c216e5adSMichael Roth         count = QGA_READ_COUNT_DEFAULT;
198c216e5adSMichael Roth     } else if (count < 0) {
199c216e5adSMichael Roth         error_set(err, QERR_INVALID_PARAMETER, "count");
200c216e5adSMichael Roth         return NULL;
201c216e5adSMichael Roth     }
202c216e5adSMichael Roth 
203c216e5adSMichael Roth     fh = gfh->fh;
204c216e5adSMichael Roth     buf = g_malloc0(count+1);
205c216e5adSMichael Roth     read_count = fread(buf, 1, count, fh);
206c216e5adSMichael Roth     if (ferror(fh)) {
207c216e5adSMichael Roth         slog("guest-file-read failed, handle: %ld", handle);
208c216e5adSMichael Roth         error_set(err, QERR_QGA_COMMAND_FAILED, "fread() failed");
209c216e5adSMichael Roth     } else {
210c216e5adSMichael Roth         buf[read_count] = 0;
211c216e5adSMichael Roth         read_data = g_malloc0(sizeof(GuestFileRead));
212c216e5adSMichael Roth         read_data->count = read_count;
213c216e5adSMichael Roth         read_data->eof = feof(fh);
214c216e5adSMichael Roth         if (read_count) {
215c216e5adSMichael Roth             read_data->buf_b64 = g_base64_encode(buf, read_count);
216c216e5adSMichael Roth         }
217c216e5adSMichael Roth     }
218c216e5adSMichael Roth     g_free(buf);
219c216e5adSMichael Roth     clearerr(fh);
220c216e5adSMichael Roth 
221c216e5adSMichael Roth     return read_data;
222c216e5adSMichael Roth }
223c216e5adSMichael Roth 
224c216e5adSMichael Roth GuestFileWrite *qmp_guest_file_write(int64_t handle, const char *buf_b64,
225c216e5adSMichael Roth                                      bool has_count, int64_t count, Error **err)
226c216e5adSMichael Roth {
227c216e5adSMichael Roth     GuestFileWrite *write_data = NULL;
228c216e5adSMichael Roth     guchar *buf;
229c216e5adSMichael Roth     gsize buf_len;
230c216e5adSMichael Roth     int write_count;
231c216e5adSMichael Roth     GuestFileHandle *gfh = guest_file_handle_find(handle);
232c216e5adSMichael Roth     FILE *fh;
233c216e5adSMichael Roth 
234c216e5adSMichael Roth     if (!gfh) {
235c216e5adSMichael Roth         error_set(err, QERR_FD_NOT_FOUND, "handle");
236c216e5adSMichael Roth         return NULL;
237c216e5adSMichael Roth     }
238c216e5adSMichael Roth 
239c216e5adSMichael Roth     fh = gfh->fh;
240c216e5adSMichael Roth     buf = g_base64_decode(buf_b64, &buf_len);
241c216e5adSMichael Roth 
242c216e5adSMichael Roth     if (!has_count) {
243c216e5adSMichael Roth         count = buf_len;
244c216e5adSMichael Roth     } else if (count < 0 || count > buf_len) {
245c216e5adSMichael Roth         g_free(buf);
246c216e5adSMichael Roth         error_set(err, QERR_INVALID_PARAMETER, "count");
247c216e5adSMichael Roth         return NULL;
248c216e5adSMichael Roth     }
249c216e5adSMichael Roth 
250c216e5adSMichael Roth     write_count = fwrite(buf, 1, count, fh);
251c216e5adSMichael Roth     if (ferror(fh)) {
252c216e5adSMichael Roth         slog("guest-file-write failed, handle: %ld", handle);
253c216e5adSMichael Roth         error_set(err, QERR_QGA_COMMAND_FAILED, "fwrite() error");
254c216e5adSMichael Roth     } else {
255c216e5adSMichael Roth         write_data = g_malloc0(sizeof(GuestFileWrite));
256c216e5adSMichael Roth         write_data->count = write_count;
257c216e5adSMichael Roth         write_data->eof = feof(fh);
258c216e5adSMichael Roth     }
259c216e5adSMichael Roth     g_free(buf);
260c216e5adSMichael Roth     clearerr(fh);
261c216e5adSMichael Roth 
262c216e5adSMichael Roth     return write_data;
263c216e5adSMichael Roth }
264c216e5adSMichael Roth 
265c216e5adSMichael Roth struct GuestFileSeek *qmp_guest_file_seek(int64_t handle, int64_t offset,
266c216e5adSMichael Roth                                           int64_t whence, Error **err)
267c216e5adSMichael Roth {
268c216e5adSMichael Roth     GuestFileHandle *gfh = guest_file_handle_find(handle);
269c216e5adSMichael Roth     GuestFileSeek *seek_data = NULL;
270c216e5adSMichael Roth     FILE *fh;
271c216e5adSMichael Roth     int ret;
272c216e5adSMichael Roth 
273c216e5adSMichael Roth     if (!gfh) {
274c216e5adSMichael Roth         error_set(err, QERR_FD_NOT_FOUND, "handle");
275c216e5adSMichael Roth         return NULL;
276c216e5adSMichael Roth     }
277c216e5adSMichael Roth 
278c216e5adSMichael Roth     fh = gfh->fh;
279c216e5adSMichael Roth     ret = fseek(fh, offset, whence);
280c216e5adSMichael Roth     if (ret == -1) {
281c216e5adSMichael Roth         error_set(err, QERR_QGA_COMMAND_FAILED, strerror(errno));
282c216e5adSMichael Roth     } else {
283c216e5adSMichael Roth         seek_data = g_malloc0(sizeof(GuestFileRead));
284c216e5adSMichael Roth         seek_data->position = ftell(fh);
285c216e5adSMichael Roth         seek_data->eof = feof(fh);
286c216e5adSMichael Roth     }
287c216e5adSMichael Roth     clearerr(fh);
288c216e5adSMichael Roth 
289c216e5adSMichael Roth     return seek_data;
290c216e5adSMichael Roth }
291c216e5adSMichael Roth 
292c216e5adSMichael Roth void qmp_guest_file_flush(int64_t handle, Error **err)
293c216e5adSMichael Roth {
294c216e5adSMichael Roth     GuestFileHandle *gfh = guest_file_handle_find(handle);
295c216e5adSMichael Roth     FILE *fh;
296c216e5adSMichael Roth     int ret;
297c216e5adSMichael Roth 
298c216e5adSMichael Roth     if (!gfh) {
299c216e5adSMichael Roth         error_set(err, QERR_FD_NOT_FOUND, "handle");
300c216e5adSMichael Roth         return;
301c216e5adSMichael Roth     }
302c216e5adSMichael Roth 
303c216e5adSMichael Roth     fh = gfh->fh;
304c216e5adSMichael Roth     ret = fflush(fh);
305c216e5adSMichael Roth     if (ret == EOF) {
306c216e5adSMichael Roth         error_set(err, QERR_QGA_COMMAND_FAILED, strerror(errno));
307c216e5adSMichael Roth     }
308c216e5adSMichael Roth }
309c216e5adSMichael Roth 
310c216e5adSMichael Roth static void guest_file_init(void)
311c216e5adSMichael Roth {
312c216e5adSMichael Roth     QTAILQ_INIT(&guest_file_state.filehandles);
313c216e5adSMichael Roth }
314c216e5adSMichael Roth 
315e72c3f2eSMichael Roth /* linux-specific implementations. avoid this if at all possible. */
316e72c3f2eSMichael Roth #if defined(__linux__)
317e72c3f2eSMichael Roth 
318eab5fd59SPaolo Bonzini #if defined(CONFIG_FSFREEZE) || defined(CONFIG_FSTRIM)
319af02203fSPaolo Bonzini typedef struct FsMount {
320c216e5adSMichael Roth     char *dirname;
321c216e5adSMichael Roth     char *devtype;
322af02203fSPaolo Bonzini     QTAILQ_ENTRY(FsMount) next;
323af02203fSPaolo Bonzini } FsMount;
324c216e5adSMichael Roth 
325af02203fSPaolo Bonzini typedef QTAILQ_HEAD(, FsMount) FsMountList;
3269e8aded4SMichael Roth 
327af02203fSPaolo Bonzini static void free_fs_mount_list(FsMountList *mounts)
328c216e5adSMichael Roth {
329af02203fSPaolo Bonzini      FsMount *mount, *temp;
330c216e5adSMichael Roth 
3319e8aded4SMichael Roth      if (!mounts) {
3329e8aded4SMichael Roth          return;
3339e8aded4SMichael Roth      }
3349e8aded4SMichael Roth 
3359e8aded4SMichael Roth      QTAILQ_FOREACH_SAFE(mount, mounts, next, temp) {
3369e8aded4SMichael Roth          QTAILQ_REMOVE(mounts, mount, next);
337c216e5adSMichael Roth          g_free(mount->dirname);
338c216e5adSMichael Roth          g_free(mount->devtype);
339c216e5adSMichael Roth          g_free(mount);
340c216e5adSMichael Roth      }
3419e8aded4SMichael Roth }
3429e8aded4SMichael Roth 
3439e8aded4SMichael Roth /*
3449e8aded4SMichael Roth  * Walk the mount table and build a list of local file systems
3459e8aded4SMichael Roth  */
346af02203fSPaolo Bonzini static int build_fs_mount_list(FsMountList *mounts)
3479e8aded4SMichael Roth {
3489e8aded4SMichael Roth     struct mntent *ment;
349af02203fSPaolo Bonzini     FsMount *mount;
3509e2fa418SMichael Roth     char const *mtab = "/proc/self/mounts";
3519e8aded4SMichael Roth     FILE *fp;
352c216e5adSMichael Roth 
353c216e5adSMichael Roth     fp = setmntent(mtab, "r");
354c216e5adSMichael Roth     if (!fp) {
355c216e5adSMichael Roth         g_warning("fsfreeze: unable to read mtab");
356c216e5adSMichael Roth         return -1;
357c216e5adSMichael Roth     }
358c216e5adSMichael Roth 
359c216e5adSMichael Roth     while ((ment = getmntent(fp))) {
360c216e5adSMichael Roth         /*
361c216e5adSMichael Roth          * An entry which device name doesn't start with a '/' is
362c216e5adSMichael Roth          * either a dummy file system or a network file system.
363c216e5adSMichael Roth          * Add special handling for smbfs and cifs as is done by
364c216e5adSMichael Roth          * coreutils as well.
365c216e5adSMichael Roth          */
366c216e5adSMichael Roth         if ((ment->mnt_fsname[0] != '/') ||
367c216e5adSMichael Roth             (strcmp(ment->mnt_type, "smbfs") == 0) ||
368c216e5adSMichael Roth             (strcmp(ment->mnt_type, "cifs") == 0)) {
369c216e5adSMichael Roth             continue;
370c216e5adSMichael Roth         }
371c216e5adSMichael Roth 
372af02203fSPaolo Bonzini         mount = g_malloc0(sizeof(FsMount));
373c216e5adSMichael Roth         mount->dirname = g_strdup(ment->mnt_dir);
374c216e5adSMichael Roth         mount->devtype = g_strdup(ment->mnt_type);
375c216e5adSMichael Roth 
3769e8aded4SMichael Roth         QTAILQ_INSERT_TAIL(mounts, mount, next);
377c216e5adSMichael Roth     }
378c216e5adSMichael Roth 
379c216e5adSMichael Roth     endmntent(fp);
380c216e5adSMichael Roth 
381c216e5adSMichael Roth     return 0;
382c216e5adSMichael Roth }
383eab5fd59SPaolo Bonzini #endif
384eab5fd59SPaolo Bonzini 
385eab5fd59SPaolo Bonzini #if defined(CONFIG_FSFREEZE)
386c216e5adSMichael Roth 
387c216e5adSMichael Roth /*
388c216e5adSMichael Roth  * Return status of freeze/thaw
389c216e5adSMichael Roth  */
390c216e5adSMichael Roth GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **err)
391c216e5adSMichael Roth {
392f22d85e9SMichael Roth     if (ga_is_frozen(ga_state)) {
393f22d85e9SMichael Roth         return GUEST_FSFREEZE_STATUS_FROZEN;
394f22d85e9SMichael Roth     }
395f22d85e9SMichael Roth 
396f22d85e9SMichael Roth     return GUEST_FSFREEZE_STATUS_THAWED;
397c216e5adSMichael Roth }
398c216e5adSMichael Roth 
399c216e5adSMichael Roth /*
400c216e5adSMichael Roth  * Walk list of mounted file systems in the guest, and freeze the ones which
401c216e5adSMichael Roth  * are real local file systems.
402c216e5adSMichael Roth  */
403c216e5adSMichael Roth int64_t qmp_guest_fsfreeze_freeze(Error **err)
404c216e5adSMichael Roth {
405c216e5adSMichael Roth     int ret = 0, i = 0;
406af02203fSPaolo Bonzini     FsMountList mounts;
407af02203fSPaolo Bonzini     struct FsMount *mount;
408c216e5adSMichael Roth     int fd;
409c216e5adSMichael Roth     char err_msg[512];
410c216e5adSMichael Roth 
411c216e5adSMichael Roth     slog("guest-fsfreeze called");
412c216e5adSMichael Roth 
4139e8aded4SMichael Roth     QTAILQ_INIT(&mounts);
414af02203fSPaolo Bonzini     ret = build_fs_mount_list(&mounts);
415c216e5adSMichael Roth     if (ret < 0) {
416c216e5adSMichael Roth         return ret;
417c216e5adSMichael Roth     }
418c216e5adSMichael Roth 
419c216e5adSMichael Roth     /* cannot risk guest agent blocking itself on a write in this state */
420f22d85e9SMichael Roth     ga_set_frozen(ga_state);
421c216e5adSMichael Roth 
4229e8aded4SMichael Roth     QTAILQ_FOREACH(mount, &mounts, next) {
423c216e5adSMichael Roth         fd = qemu_open(mount->dirname, O_RDONLY);
424c216e5adSMichael Roth         if (fd == -1) {
4259e8aded4SMichael Roth             sprintf(err_msg, "failed to open %s, %s", mount->dirname,
4269e8aded4SMichael Roth                     strerror(errno));
427c216e5adSMichael Roth             error_set(err, QERR_QGA_COMMAND_FAILED, err_msg);
428c216e5adSMichael Roth             goto error;
429c216e5adSMichael Roth         }
430c216e5adSMichael Roth 
431c216e5adSMichael Roth         /* we try to cull filesytems we know won't work in advance, but other
432c216e5adSMichael Roth          * filesytems may not implement fsfreeze for less obvious reasons.
4339e8aded4SMichael Roth          * these will report EOPNOTSUPP. we simply ignore these when tallying
4349e8aded4SMichael Roth          * the number of frozen filesystems.
4359e8aded4SMichael Roth          *
4369e8aded4SMichael Roth          * any other error means a failure to freeze a filesystem we
4379e8aded4SMichael Roth          * expect to be freezable, so return an error in those cases
4389e8aded4SMichael Roth          * and return system to thawed state.
439c216e5adSMichael Roth          */
440c216e5adSMichael Roth         ret = ioctl(fd, FIFREEZE);
4419e8aded4SMichael Roth         if (ret == -1) {
4429e8aded4SMichael Roth             if (errno != EOPNOTSUPP) {
4439e8aded4SMichael Roth                 sprintf(err_msg, "failed to freeze %s, %s",
4449e8aded4SMichael Roth                         mount->dirname, strerror(errno));
445c216e5adSMichael Roth                 error_set(err, QERR_QGA_COMMAND_FAILED, err_msg);
446c216e5adSMichael Roth                 close(fd);
447c216e5adSMichael Roth                 goto error;
448c216e5adSMichael Roth             }
4499e8aded4SMichael Roth         } else {
450c216e5adSMichael Roth             i++;
451c216e5adSMichael Roth         }
4529e8aded4SMichael Roth         close(fd);
4539e8aded4SMichael Roth     }
454c216e5adSMichael Roth 
455af02203fSPaolo Bonzini     free_fs_mount_list(&mounts);
456c216e5adSMichael Roth     return i;
457c216e5adSMichael Roth 
458c216e5adSMichael Roth error:
459af02203fSPaolo Bonzini     free_fs_mount_list(&mounts);
460c216e5adSMichael Roth     qmp_guest_fsfreeze_thaw(NULL);
461c216e5adSMichael Roth     return 0;
462c216e5adSMichael Roth }
463c216e5adSMichael Roth 
464c216e5adSMichael Roth /*
465c216e5adSMichael Roth  * Walk list of frozen file systems in the guest, and thaw them.
466c216e5adSMichael Roth  */
467c216e5adSMichael Roth int64_t qmp_guest_fsfreeze_thaw(Error **err)
468c216e5adSMichael Roth {
469c216e5adSMichael Roth     int ret;
470af02203fSPaolo Bonzini     FsMountList mounts;
471af02203fSPaolo Bonzini     FsMount *mount;
4729e8aded4SMichael Roth     int fd, i = 0, logged;
473c216e5adSMichael Roth 
4749e8aded4SMichael Roth     QTAILQ_INIT(&mounts);
475af02203fSPaolo Bonzini     ret = build_fs_mount_list(&mounts);
4769e8aded4SMichael Roth     if (ret) {
4779e8aded4SMichael Roth         error_set(err, QERR_QGA_COMMAND_FAILED,
4789e8aded4SMichael Roth                   "failed to enumerate filesystems");
4799e8aded4SMichael Roth         return 0;
4809e8aded4SMichael Roth     }
4819e8aded4SMichael Roth 
4829e8aded4SMichael Roth     QTAILQ_FOREACH(mount, &mounts, next) {
4839e8aded4SMichael Roth         logged = false;
484c216e5adSMichael Roth         fd = qemu_open(mount->dirname, O_RDONLY);
485c216e5adSMichael Roth         if (fd == -1) {
486c216e5adSMichael Roth             continue;
487c216e5adSMichael Roth         }
4889e8aded4SMichael Roth         /* we have no way of knowing whether a filesystem was actually unfrozen
4899e8aded4SMichael Roth          * as a result of a successful call to FITHAW, only that if an error
4909e8aded4SMichael Roth          * was returned the filesystem was *not* unfrozen by that particular
4919e8aded4SMichael Roth          * call.
4929e8aded4SMichael Roth          *
493a31f0531SJim Meyering          * since multiple preceding FIFREEZEs require multiple calls to FITHAW
4949e8aded4SMichael Roth          * to unfreeze, continuing issuing FITHAW until an error is returned,
4959e8aded4SMichael Roth          * in which case either the filesystem is in an unfreezable state, or,
4969e8aded4SMichael Roth          * more likely, it was thawed previously (and remains so afterward).
4979e8aded4SMichael Roth          *
4989e8aded4SMichael Roth          * also, since the most recent successful call is the one that did
4999e8aded4SMichael Roth          * the actual unfreeze, we can use this to provide an accurate count
5009e8aded4SMichael Roth          * of the number of filesystems unfrozen by guest-fsfreeze-thaw, which
5019e8aded4SMichael Roth          * may * be useful for determining whether a filesystem was unfrozen
5029e8aded4SMichael Roth          * during the freeze/thaw phase by a process other than qemu-ga.
5039e8aded4SMichael Roth          */
5049e8aded4SMichael Roth         do {
505c216e5adSMichael Roth             ret = ioctl(fd, FITHAW);
5069e8aded4SMichael Roth             if (ret == 0 && !logged) {
507c216e5adSMichael Roth                 i++;
5089e8aded4SMichael Roth                 logged = true;
5099e8aded4SMichael Roth             }
5109e8aded4SMichael Roth         } while (ret == 0);
5119e8aded4SMichael Roth         close(fd);
512c216e5adSMichael Roth     }
513c216e5adSMichael Roth 
514f22d85e9SMichael Roth     ga_unset_frozen(ga_state);
515af02203fSPaolo Bonzini     free_fs_mount_list(&mounts);
516c216e5adSMichael Roth     return i;
517c216e5adSMichael Roth }
518c216e5adSMichael Roth 
519c216e5adSMichael Roth static void guest_fsfreeze_cleanup(void)
520c216e5adSMichael Roth {
521c216e5adSMichael Roth     int64_t ret;
522c216e5adSMichael Roth     Error *err = NULL;
523c216e5adSMichael Roth 
524f22d85e9SMichael Roth     if (ga_is_frozen(ga_state) == GUEST_FSFREEZE_STATUS_FROZEN) {
525c216e5adSMichael Roth         ret = qmp_guest_fsfreeze_thaw(&err);
526c216e5adSMichael Roth         if (ret < 0 || err) {
527c216e5adSMichael Roth             slog("failed to clean up frozen filesystems");
528c216e5adSMichael Roth         }
529c216e5adSMichael Roth     }
530c216e5adSMichael Roth }
531e72c3f2eSMichael Roth #endif /* CONFIG_FSFREEZE */
532c216e5adSMichael Roth 
533eab5fd59SPaolo Bonzini #if defined(CONFIG_FSTRIM)
534eab5fd59SPaolo Bonzini /*
535eab5fd59SPaolo Bonzini  * Walk list of mounted file systems in the guest, and trim them.
536eab5fd59SPaolo Bonzini  */
537eab5fd59SPaolo Bonzini void qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **err)
538eab5fd59SPaolo Bonzini {
539eab5fd59SPaolo Bonzini     int ret = 0;
540eab5fd59SPaolo Bonzini     FsMountList mounts;
541eab5fd59SPaolo Bonzini     struct FsMount *mount;
542eab5fd59SPaolo Bonzini     int fd;
543eab5fd59SPaolo Bonzini     char err_msg[512];
544eab5fd59SPaolo Bonzini     struct fstrim_range r = {
545eab5fd59SPaolo Bonzini         .start = 0,
546eab5fd59SPaolo Bonzini         .len = -1,
547eab5fd59SPaolo Bonzini         .minlen = has_minimum ? minimum : 0,
548eab5fd59SPaolo Bonzini     };
549eab5fd59SPaolo Bonzini 
550eab5fd59SPaolo Bonzini     slog("guest-fstrim called");
551eab5fd59SPaolo Bonzini 
552eab5fd59SPaolo Bonzini     QTAILQ_INIT(&mounts);
553eab5fd59SPaolo Bonzini     ret = build_fs_mount_list(&mounts);
554eab5fd59SPaolo Bonzini     if (ret < 0) {
555eab5fd59SPaolo Bonzini         return;
556eab5fd59SPaolo Bonzini     }
557eab5fd59SPaolo Bonzini 
558eab5fd59SPaolo Bonzini     QTAILQ_FOREACH(mount, &mounts, next) {
559eab5fd59SPaolo Bonzini         fd = qemu_open(mount->dirname, O_RDONLY);
560eab5fd59SPaolo Bonzini         if (fd == -1) {
561eab5fd59SPaolo Bonzini             sprintf(err_msg, "failed to open %s, %s", mount->dirname,
562eab5fd59SPaolo Bonzini                     strerror(errno));
563eab5fd59SPaolo Bonzini             error_set(err, QERR_QGA_COMMAND_FAILED, err_msg);
564eab5fd59SPaolo Bonzini             goto error;
565eab5fd59SPaolo Bonzini         }
566eab5fd59SPaolo Bonzini 
567eab5fd59SPaolo Bonzini         /* We try to cull filesytems we know won't work in advance, but other
568eab5fd59SPaolo Bonzini          * filesytems may not implement fstrim for less obvious reasons.  These
569eab5fd59SPaolo Bonzini          * will report EOPNOTSUPP; we simply ignore these errors.  Any other
570eab5fd59SPaolo Bonzini          * error means an unexpected error, so return it in those cases.  In
571eab5fd59SPaolo Bonzini          * some other cases ENOTTY will be reported (e.g. CD-ROMs).
572eab5fd59SPaolo Bonzini          */
573eab5fd59SPaolo Bonzini         ret = ioctl(fd, FITRIM, &r);
574eab5fd59SPaolo Bonzini         if (ret == -1) {
575eab5fd59SPaolo Bonzini             if (errno != ENOTTY && errno != EOPNOTSUPP) {
576eab5fd59SPaolo Bonzini                 sprintf(err_msg, "failed to trim %s, %s",
577eab5fd59SPaolo Bonzini                         mount->dirname, strerror(errno));
578eab5fd59SPaolo Bonzini                 error_set(err, QERR_QGA_COMMAND_FAILED, err_msg);
579eab5fd59SPaolo Bonzini                 close(fd);
580eab5fd59SPaolo Bonzini                 goto error;
581eab5fd59SPaolo Bonzini             }
582eab5fd59SPaolo Bonzini         }
583eab5fd59SPaolo Bonzini         close(fd);
584eab5fd59SPaolo Bonzini     }
585eab5fd59SPaolo Bonzini 
586eab5fd59SPaolo Bonzini error:
587eab5fd59SPaolo Bonzini     free_fs_mount_list(&mounts);
588eab5fd59SPaolo Bonzini }
589eab5fd59SPaolo Bonzini #endif /* CONFIG_FSTRIM */
590eab5fd59SPaolo Bonzini 
591eab5fd59SPaolo Bonzini 
59211d0f125SLuiz Capitulino #define LINUX_SYS_STATE_FILE "/sys/power/state"
59311d0f125SLuiz Capitulino #define SUSPEND_SUPPORTED 0
59411d0f125SLuiz Capitulino #define SUSPEND_NOT_SUPPORTED 1
59511d0f125SLuiz Capitulino 
59611d0f125SLuiz Capitulino static void bios_supports_mode(const char *pmutils_bin, const char *pmutils_arg,
59711d0f125SLuiz Capitulino                                const char *sysfile_str, Error **err)
59811d0f125SLuiz Capitulino {
59911d0f125SLuiz Capitulino     char *pmutils_path;
600dc8764f0SLuiz Capitulino     pid_t pid, rpid;
601dc8764f0SLuiz Capitulino     int status;
60211d0f125SLuiz Capitulino 
60311d0f125SLuiz Capitulino     pmutils_path = g_find_program_in_path(pmutils_bin);
60411d0f125SLuiz Capitulino 
60511d0f125SLuiz Capitulino     pid = fork();
60611d0f125SLuiz Capitulino     if (!pid) {
607dc8764f0SLuiz Capitulino         char buf[32]; /* hopefully big enough */
608dc8764f0SLuiz Capitulino         ssize_t ret;
609dc8764f0SLuiz Capitulino         int fd;
61011d0f125SLuiz Capitulino 
61111d0f125SLuiz Capitulino         setsid();
61211d0f125SLuiz Capitulino         reopen_fd_to_null(0);
61311d0f125SLuiz Capitulino         reopen_fd_to_null(1);
61411d0f125SLuiz Capitulino         reopen_fd_to_null(2);
61511d0f125SLuiz Capitulino 
61611d0f125SLuiz Capitulino         if (pmutils_path) {
61711d0f125SLuiz Capitulino             execle(pmutils_path, pmutils_bin, pmutils_arg, NULL, environ);
61811d0f125SLuiz Capitulino         }
61911d0f125SLuiz Capitulino 
62011d0f125SLuiz Capitulino         /*
62111d0f125SLuiz Capitulino          * If we get here either pm-utils is not installed or execle() has
62211d0f125SLuiz Capitulino          * failed. Let's try the manual method if the caller wants it.
62311d0f125SLuiz Capitulino          */
62411d0f125SLuiz Capitulino 
62511d0f125SLuiz Capitulino         if (!sysfile_str) {
62611d0f125SLuiz Capitulino             _exit(SUSPEND_NOT_SUPPORTED);
62711d0f125SLuiz Capitulino         }
62811d0f125SLuiz Capitulino 
62911d0f125SLuiz Capitulino         fd = open(LINUX_SYS_STATE_FILE, O_RDONLY);
63011d0f125SLuiz Capitulino         if (fd < 0) {
63111d0f125SLuiz Capitulino             _exit(SUSPEND_NOT_SUPPORTED);
63211d0f125SLuiz Capitulino         }
63311d0f125SLuiz Capitulino 
63411d0f125SLuiz Capitulino         ret = read(fd, buf, sizeof(buf)-1);
63511d0f125SLuiz Capitulino         if (ret <= 0) {
63611d0f125SLuiz Capitulino             _exit(SUSPEND_NOT_SUPPORTED);
63711d0f125SLuiz Capitulino         }
63811d0f125SLuiz Capitulino         buf[ret] = '\0';
63911d0f125SLuiz Capitulino 
64011d0f125SLuiz Capitulino         if (strstr(buf, sysfile_str)) {
64111d0f125SLuiz Capitulino             _exit(SUSPEND_SUPPORTED);
64211d0f125SLuiz Capitulino         }
64311d0f125SLuiz Capitulino 
64411d0f125SLuiz Capitulino         _exit(SUSPEND_NOT_SUPPORTED);
64511d0f125SLuiz Capitulino     }
64611d0f125SLuiz Capitulino 
64711d0f125SLuiz Capitulino     g_free(pmutils_path);
64811d0f125SLuiz Capitulino 
64911d0f125SLuiz Capitulino     if (pid < 0) {
650dc8764f0SLuiz Capitulino         goto undef_err;
65111d0f125SLuiz Capitulino     }
65211d0f125SLuiz Capitulino 
653dc8764f0SLuiz Capitulino     do {
654dc8764f0SLuiz Capitulino         rpid = waitpid(pid, &status, 0);
655dc8764f0SLuiz Capitulino     } while (rpid == -1 && errno == EINTR);
656dc8764f0SLuiz Capitulino     if (rpid == pid && WIFEXITED(status)) {
657dc8764f0SLuiz Capitulino         switch (WEXITSTATUS(status)) {
658dc8764f0SLuiz Capitulino         case SUSPEND_SUPPORTED:
659dc8764f0SLuiz Capitulino             return;
660dc8764f0SLuiz Capitulino         case SUSPEND_NOT_SUPPORTED:
66111d0f125SLuiz Capitulino             error_set(err, QERR_UNSUPPORTED);
662dc8764f0SLuiz Capitulino             return;
663dc8764f0SLuiz Capitulino         default:
664dc8764f0SLuiz Capitulino             goto undef_err;
665dc8764f0SLuiz Capitulino         }
666dc8764f0SLuiz Capitulino     }
66711d0f125SLuiz Capitulino 
668dc8764f0SLuiz Capitulino undef_err:
669dc8764f0SLuiz Capitulino     error_set(err, QERR_UNDEFINED_ERROR);
67011d0f125SLuiz Capitulino }
67111d0f125SLuiz Capitulino 
67211d0f125SLuiz Capitulino static void guest_suspend(const char *pmutils_bin, const char *sysfile_str,
67311d0f125SLuiz Capitulino                           Error **err)
67411d0f125SLuiz Capitulino {
67511d0f125SLuiz Capitulino     char *pmutils_path;
676dc8764f0SLuiz Capitulino     pid_t rpid, pid;
677dc8764f0SLuiz Capitulino     int status;
67811d0f125SLuiz Capitulino 
67911d0f125SLuiz Capitulino     pmutils_path = g_find_program_in_path(pmutils_bin);
68011d0f125SLuiz Capitulino 
68111d0f125SLuiz Capitulino     pid = fork();
68211d0f125SLuiz Capitulino     if (pid == 0) {
68311d0f125SLuiz Capitulino         /* child */
68411d0f125SLuiz Capitulino         int fd;
68511d0f125SLuiz Capitulino 
68611d0f125SLuiz Capitulino         setsid();
68711d0f125SLuiz Capitulino         reopen_fd_to_null(0);
68811d0f125SLuiz Capitulino         reopen_fd_to_null(1);
68911d0f125SLuiz Capitulino         reopen_fd_to_null(2);
69011d0f125SLuiz Capitulino 
69111d0f125SLuiz Capitulino         if (pmutils_path) {
69211d0f125SLuiz Capitulino             execle(pmutils_path, pmutils_bin, NULL, environ);
69311d0f125SLuiz Capitulino         }
69411d0f125SLuiz Capitulino 
69511d0f125SLuiz Capitulino         /*
69611d0f125SLuiz Capitulino          * If we get here either pm-utils is not installed or execle() has
69711d0f125SLuiz Capitulino          * failed. Let's try the manual method if the caller wants it.
69811d0f125SLuiz Capitulino          */
69911d0f125SLuiz Capitulino 
70011d0f125SLuiz Capitulino         if (!sysfile_str) {
70111d0f125SLuiz Capitulino             _exit(EXIT_FAILURE);
70211d0f125SLuiz Capitulino         }
70311d0f125SLuiz Capitulino 
70411d0f125SLuiz Capitulino         fd = open(LINUX_SYS_STATE_FILE, O_WRONLY);
70511d0f125SLuiz Capitulino         if (fd < 0) {
70611d0f125SLuiz Capitulino             _exit(EXIT_FAILURE);
70711d0f125SLuiz Capitulino         }
70811d0f125SLuiz Capitulino 
70911d0f125SLuiz Capitulino         if (write(fd, sysfile_str, strlen(sysfile_str)) < 0) {
71011d0f125SLuiz Capitulino             _exit(EXIT_FAILURE);
71111d0f125SLuiz Capitulino         }
71211d0f125SLuiz Capitulino 
71311d0f125SLuiz Capitulino         _exit(EXIT_SUCCESS);
71411d0f125SLuiz Capitulino     }
71511d0f125SLuiz Capitulino 
71611d0f125SLuiz Capitulino     g_free(pmutils_path);
71711d0f125SLuiz Capitulino 
71811d0f125SLuiz Capitulino     if (pid < 0) {
719dc8764f0SLuiz Capitulino         goto exit_err;
720dc8764f0SLuiz Capitulino     }
721dc8764f0SLuiz Capitulino 
722dc8764f0SLuiz Capitulino     do {
723dc8764f0SLuiz Capitulino         rpid = waitpid(pid, &status, 0);
724dc8764f0SLuiz Capitulino     } while (rpid == -1 && errno == EINTR);
725dc8764f0SLuiz Capitulino     if (rpid == pid && WIFEXITED(status) && !WEXITSTATUS(status)) {
72611d0f125SLuiz Capitulino         return;
72711d0f125SLuiz Capitulino     }
728dc8764f0SLuiz Capitulino 
729dc8764f0SLuiz Capitulino exit_err:
730dc8764f0SLuiz Capitulino     error_set(err, QERR_UNDEFINED_ERROR);
73111d0f125SLuiz Capitulino }
73211d0f125SLuiz Capitulino 
73311d0f125SLuiz Capitulino void qmp_guest_suspend_disk(Error **err)
73411d0f125SLuiz Capitulino {
73511d0f125SLuiz Capitulino     bios_supports_mode("pm-is-supported", "--hibernate", "disk", err);
73611d0f125SLuiz Capitulino     if (error_is_set(err)) {
73711d0f125SLuiz Capitulino         return;
73811d0f125SLuiz Capitulino     }
73911d0f125SLuiz Capitulino 
74011d0f125SLuiz Capitulino     guest_suspend("pm-hibernate", "disk", err);
74111d0f125SLuiz Capitulino }
74211d0f125SLuiz Capitulino 
743fbf42210SLuiz Capitulino void qmp_guest_suspend_ram(Error **err)
744fbf42210SLuiz Capitulino {
745fbf42210SLuiz Capitulino     bios_supports_mode("pm-is-supported", "--suspend", "mem", err);
746fbf42210SLuiz Capitulino     if (error_is_set(err)) {
747fbf42210SLuiz Capitulino         return;
748fbf42210SLuiz Capitulino     }
749fbf42210SLuiz Capitulino 
750fbf42210SLuiz Capitulino     guest_suspend("pm-suspend", "mem", err);
751fbf42210SLuiz Capitulino }
752fbf42210SLuiz Capitulino 
75395f4f404SLuiz Capitulino void qmp_guest_suspend_hybrid(Error **err)
75495f4f404SLuiz Capitulino {
75595f4f404SLuiz Capitulino     bios_supports_mode("pm-is-supported", "--suspend-hybrid", NULL, err);
75695f4f404SLuiz Capitulino     if (error_is_set(err)) {
75795f4f404SLuiz Capitulino         return;
75895f4f404SLuiz Capitulino     }
75995f4f404SLuiz Capitulino 
76095f4f404SLuiz Capitulino     guest_suspend("pm-suspend-hybrid", NULL, err);
76195f4f404SLuiz Capitulino }
76295f4f404SLuiz Capitulino 
7633424fc9fSMichal Privoznik static GuestNetworkInterfaceList *
7643424fc9fSMichal Privoznik guest_find_interface(GuestNetworkInterfaceList *head,
7653424fc9fSMichal Privoznik                      const char *name)
7663424fc9fSMichal Privoznik {
7673424fc9fSMichal Privoznik     for (; head; head = head->next) {
7683424fc9fSMichal Privoznik         if (strcmp(head->value->name, name) == 0) {
7693424fc9fSMichal Privoznik             break;
7703424fc9fSMichal Privoznik         }
7713424fc9fSMichal Privoznik     }
7723424fc9fSMichal Privoznik 
7733424fc9fSMichal Privoznik     return head;
7743424fc9fSMichal Privoznik }
7753424fc9fSMichal Privoznik 
7763424fc9fSMichal Privoznik /*
7773424fc9fSMichal Privoznik  * Build information about guest interfaces
7783424fc9fSMichal Privoznik  */
7793424fc9fSMichal Privoznik GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp)
7803424fc9fSMichal Privoznik {
7813424fc9fSMichal Privoznik     GuestNetworkInterfaceList *head = NULL, *cur_item = NULL;
7823424fc9fSMichal Privoznik     struct ifaddrs *ifap, *ifa;
7833424fc9fSMichal Privoznik     char err_msg[512];
7843424fc9fSMichal Privoznik 
7853424fc9fSMichal Privoznik     if (getifaddrs(&ifap) < 0) {
7863424fc9fSMichal Privoznik         snprintf(err_msg, sizeof(err_msg),
7873424fc9fSMichal Privoznik                  "getifaddrs failed: %s", strerror(errno));
7883424fc9fSMichal Privoznik         error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg);
7893424fc9fSMichal Privoznik         goto error;
7903424fc9fSMichal Privoznik     }
7913424fc9fSMichal Privoznik 
7923424fc9fSMichal Privoznik     for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
7933424fc9fSMichal Privoznik         GuestNetworkInterfaceList *info;
7943424fc9fSMichal Privoznik         GuestIpAddressList **address_list = NULL, *address_item = NULL;
7953424fc9fSMichal Privoznik         char addr4[INET_ADDRSTRLEN];
7963424fc9fSMichal Privoznik         char addr6[INET6_ADDRSTRLEN];
7973424fc9fSMichal Privoznik         int sock;
7983424fc9fSMichal Privoznik         struct ifreq ifr;
7993424fc9fSMichal Privoznik         unsigned char *mac_addr;
8003424fc9fSMichal Privoznik         void *p;
8013424fc9fSMichal Privoznik 
8023424fc9fSMichal Privoznik         g_debug("Processing %s interface", ifa->ifa_name);
8033424fc9fSMichal Privoznik 
8043424fc9fSMichal Privoznik         info = guest_find_interface(head, ifa->ifa_name);
8053424fc9fSMichal Privoznik 
8063424fc9fSMichal Privoznik         if (!info) {
8073424fc9fSMichal Privoznik             info = g_malloc0(sizeof(*info));
8083424fc9fSMichal Privoznik             info->value = g_malloc0(sizeof(*info->value));
8093424fc9fSMichal Privoznik             info->value->name = g_strdup(ifa->ifa_name);
8103424fc9fSMichal Privoznik 
8113424fc9fSMichal Privoznik             if (!cur_item) {
8123424fc9fSMichal Privoznik                 head = cur_item = info;
8133424fc9fSMichal Privoznik             } else {
8143424fc9fSMichal Privoznik                 cur_item->next = info;
8153424fc9fSMichal Privoznik                 cur_item = info;
8163424fc9fSMichal Privoznik             }
8173424fc9fSMichal Privoznik         }
8183424fc9fSMichal Privoznik 
8193424fc9fSMichal Privoznik         if (!info->value->has_hardware_address &&
8203424fc9fSMichal Privoznik             ifa->ifa_flags & SIOCGIFHWADDR) {
8213424fc9fSMichal Privoznik             /* we haven't obtained HW address yet */
8223424fc9fSMichal Privoznik             sock = socket(PF_INET, SOCK_STREAM, 0);
8233424fc9fSMichal Privoznik             if (sock == -1) {
8243424fc9fSMichal Privoznik                 snprintf(err_msg, sizeof(err_msg),
8253424fc9fSMichal Privoznik                          "failed to create socket: %s", strerror(errno));
8263424fc9fSMichal Privoznik                 error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg);
8273424fc9fSMichal Privoznik                 goto error;
8283424fc9fSMichal Privoznik             }
8293424fc9fSMichal Privoznik 
8303424fc9fSMichal Privoznik             memset(&ifr, 0, sizeof(ifr));
8311ab516edSJim Meyering             pstrcpy(ifr.ifr_name, IF_NAMESIZE, info->value->name);
8323424fc9fSMichal Privoznik             if (ioctl(sock, SIOCGIFHWADDR, &ifr) == -1) {
8333424fc9fSMichal Privoznik                 snprintf(err_msg, sizeof(err_msg),
834a31f0531SJim Meyering                          "failed to get MAC address of %s: %s",
8353424fc9fSMichal Privoznik                          ifa->ifa_name,
8363424fc9fSMichal Privoznik                          strerror(errno));
8373424fc9fSMichal Privoznik                 error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg);
8383424fc9fSMichal Privoznik                 goto error;
8393424fc9fSMichal Privoznik             }
8403424fc9fSMichal Privoznik 
8413424fc9fSMichal Privoznik             mac_addr = (unsigned char *) &ifr.ifr_hwaddr.sa_data;
8423424fc9fSMichal Privoznik 
8433424fc9fSMichal Privoznik             if (asprintf(&info->value->hardware_address,
8443424fc9fSMichal Privoznik                          "%02x:%02x:%02x:%02x:%02x:%02x",
8453424fc9fSMichal Privoznik                          (int) mac_addr[0], (int) mac_addr[1],
8463424fc9fSMichal Privoznik                          (int) mac_addr[2], (int) mac_addr[3],
8473424fc9fSMichal Privoznik                          (int) mac_addr[4], (int) mac_addr[5]) == -1) {
8483424fc9fSMichal Privoznik                 snprintf(err_msg, sizeof(err_msg),
8493424fc9fSMichal Privoznik                          "failed to format MAC: %s", strerror(errno));
8503424fc9fSMichal Privoznik                 error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg);
8513424fc9fSMichal Privoznik                 goto error;
8523424fc9fSMichal Privoznik             }
8533424fc9fSMichal Privoznik 
8543424fc9fSMichal Privoznik             info->value->has_hardware_address = true;
8553424fc9fSMichal Privoznik             close(sock);
8563424fc9fSMichal Privoznik         }
8573424fc9fSMichal Privoznik 
8583424fc9fSMichal Privoznik         if (ifa->ifa_addr &&
8593424fc9fSMichal Privoznik             ifa->ifa_addr->sa_family == AF_INET) {
8603424fc9fSMichal Privoznik             /* interface with IPv4 address */
8613424fc9fSMichal Privoznik             address_item = g_malloc0(sizeof(*address_item));
8623424fc9fSMichal Privoznik             address_item->value = g_malloc0(sizeof(*address_item->value));
8633424fc9fSMichal Privoznik             p = &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
8643424fc9fSMichal Privoznik             if (!inet_ntop(AF_INET, p, addr4, sizeof(addr4))) {
8653424fc9fSMichal Privoznik                 snprintf(err_msg, sizeof(err_msg),
8663424fc9fSMichal Privoznik                          "inet_ntop failed : %s", strerror(errno));
8673424fc9fSMichal Privoznik                 error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg);
8683424fc9fSMichal Privoznik                 goto error;
8693424fc9fSMichal Privoznik             }
8703424fc9fSMichal Privoznik 
8713424fc9fSMichal Privoznik             address_item->value->ip_address = g_strdup(addr4);
8723424fc9fSMichal Privoznik             address_item->value->ip_address_type = GUEST_IP_ADDRESS_TYPE_IPV4;
8733424fc9fSMichal Privoznik 
8743424fc9fSMichal Privoznik             if (ifa->ifa_netmask) {
8753424fc9fSMichal Privoznik                 /* Count the number of set bits in netmask.
8763424fc9fSMichal Privoznik                  * This is safe as '1' and '0' cannot be shuffled in netmask. */
8773424fc9fSMichal Privoznik                 p = &((struct sockaddr_in *)ifa->ifa_netmask)->sin_addr;
8783424fc9fSMichal Privoznik                 address_item->value->prefix = ctpop32(((uint32_t *) p)[0]);
8793424fc9fSMichal Privoznik             }
8803424fc9fSMichal Privoznik         } else if (ifa->ifa_addr &&
8813424fc9fSMichal Privoznik                    ifa->ifa_addr->sa_family == AF_INET6) {
8823424fc9fSMichal Privoznik             /* interface with IPv6 address */
8833424fc9fSMichal Privoznik             address_item = g_malloc0(sizeof(*address_item));
8843424fc9fSMichal Privoznik             address_item->value = g_malloc0(sizeof(*address_item->value));
8853424fc9fSMichal Privoznik             p = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
8863424fc9fSMichal Privoznik             if (!inet_ntop(AF_INET6, p, addr6, sizeof(addr6))) {
8873424fc9fSMichal Privoznik                 snprintf(err_msg, sizeof(err_msg),
8883424fc9fSMichal Privoznik                          "inet_ntop failed : %s", strerror(errno));
8893424fc9fSMichal Privoznik                 error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg);
8903424fc9fSMichal Privoznik                 goto error;
8913424fc9fSMichal Privoznik             }
8923424fc9fSMichal Privoznik 
8933424fc9fSMichal Privoznik             address_item->value->ip_address = g_strdup(addr6);
8943424fc9fSMichal Privoznik             address_item->value->ip_address_type = GUEST_IP_ADDRESS_TYPE_IPV6;
8953424fc9fSMichal Privoznik 
8963424fc9fSMichal Privoznik             if (ifa->ifa_netmask) {
8973424fc9fSMichal Privoznik                 /* Count the number of set bits in netmask.
8983424fc9fSMichal Privoznik                  * This is safe as '1' and '0' cannot be shuffled in netmask. */
8993424fc9fSMichal Privoznik                 p = &((struct sockaddr_in6 *)ifa->ifa_netmask)->sin6_addr;
9003424fc9fSMichal Privoznik                 address_item->value->prefix =
9013424fc9fSMichal Privoznik                     ctpop32(((uint32_t *) p)[0]) +
9023424fc9fSMichal Privoznik                     ctpop32(((uint32_t *) p)[1]) +
9033424fc9fSMichal Privoznik                     ctpop32(((uint32_t *) p)[2]) +
9043424fc9fSMichal Privoznik                     ctpop32(((uint32_t *) p)[3]);
9053424fc9fSMichal Privoznik             }
9063424fc9fSMichal Privoznik         }
9073424fc9fSMichal Privoznik 
9083424fc9fSMichal Privoznik         if (!address_item) {
9093424fc9fSMichal Privoznik             continue;
9103424fc9fSMichal Privoznik         }
9113424fc9fSMichal Privoznik 
9123424fc9fSMichal Privoznik         address_list = &info->value->ip_addresses;
9133424fc9fSMichal Privoznik 
9143424fc9fSMichal Privoznik         while (*address_list && (*address_list)->next) {
9153424fc9fSMichal Privoznik             address_list = &(*address_list)->next;
9163424fc9fSMichal Privoznik         }
9173424fc9fSMichal Privoznik 
9183424fc9fSMichal Privoznik         if (!*address_list) {
9193424fc9fSMichal Privoznik             *address_list = address_item;
9203424fc9fSMichal Privoznik         } else {
9213424fc9fSMichal Privoznik             (*address_list)->next = address_item;
9223424fc9fSMichal Privoznik         }
9233424fc9fSMichal Privoznik 
9243424fc9fSMichal Privoznik         info->value->has_ip_addresses = true;
9253424fc9fSMichal Privoznik 
9263424fc9fSMichal Privoznik 
9273424fc9fSMichal Privoznik     }
9283424fc9fSMichal Privoznik 
9293424fc9fSMichal Privoznik     freeifaddrs(ifap);
9303424fc9fSMichal Privoznik     return head;
9313424fc9fSMichal Privoznik 
9323424fc9fSMichal Privoznik error:
9333424fc9fSMichal Privoznik     freeifaddrs(ifap);
9343424fc9fSMichal Privoznik     qapi_free_GuestNetworkInterfaceList(head);
9353424fc9fSMichal Privoznik     return NULL;
9363424fc9fSMichal Privoznik }
9373424fc9fSMichal Privoznik 
938e72c3f2eSMichael Roth #else /* defined(__linux__) */
939e72c3f2eSMichael Roth 
940e72c3f2eSMichael Roth void qmp_guest_suspend_disk(Error **err)
941e72c3f2eSMichael Roth {
942e72c3f2eSMichael Roth     error_set(err, QERR_UNSUPPORTED);
943e72c3f2eSMichael Roth }
944e72c3f2eSMichael Roth 
945e72c3f2eSMichael Roth void qmp_guest_suspend_ram(Error **err)
946e72c3f2eSMichael Roth {
947e72c3f2eSMichael Roth     error_set(err, QERR_UNSUPPORTED);
948e72c3f2eSMichael Roth }
949e72c3f2eSMichael Roth 
950e72c3f2eSMichael Roth void qmp_guest_suspend_hybrid(Error **err)
951e72c3f2eSMichael Roth {
952e72c3f2eSMichael Roth     error_set(err, QERR_UNSUPPORTED);
953e72c3f2eSMichael Roth }
954e72c3f2eSMichael Roth 
955e72c3f2eSMichael Roth GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp)
956e72c3f2eSMichael Roth {
957e72c3f2eSMichael Roth     error_set(errp, QERR_UNSUPPORTED);
958e72c3f2eSMichael Roth     return NULL;
959e72c3f2eSMichael Roth }
960e72c3f2eSMichael Roth 
961e72c3f2eSMichael Roth #endif
962e72c3f2eSMichael Roth 
963d35d4cb5SMichael Roth #if !defined(CONFIG_FSFREEZE)
964d35d4cb5SMichael Roth 
965d35d4cb5SMichael Roth GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **err)
966d35d4cb5SMichael Roth {
967d35d4cb5SMichael Roth     error_set(err, QERR_UNSUPPORTED);
968d35d4cb5SMichael Roth 
969d35d4cb5SMichael Roth     return 0;
970d35d4cb5SMichael Roth }
971d35d4cb5SMichael Roth 
972d35d4cb5SMichael Roth int64_t qmp_guest_fsfreeze_freeze(Error **err)
973d35d4cb5SMichael Roth {
974d35d4cb5SMichael Roth     error_set(err, QERR_UNSUPPORTED);
975d35d4cb5SMichael Roth 
976d35d4cb5SMichael Roth     return 0;
977d35d4cb5SMichael Roth }
978d35d4cb5SMichael Roth 
979d35d4cb5SMichael Roth int64_t qmp_guest_fsfreeze_thaw(Error **err)
980d35d4cb5SMichael Roth {
981d35d4cb5SMichael Roth     error_set(err, QERR_UNSUPPORTED);
982d35d4cb5SMichael Roth 
983d35d4cb5SMichael Roth     return 0;
984d35d4cb5SMichael Roth }
985eab5fd59SPaolo Bonzini #endif /* CONFIG_FSFREEZE */
986d35d4cb5SMichael Roth 
987eab5fd59SPaolo Bonzini #if !defined(CONFIG_FSTRIM)
988eab5fd59SPaolo Bonzini void qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **err)
989eab5fd59SPaolo Bonzini {
990eab5fd59SPaolo Bonzini     error_set(err, QERR_UNSUPPORTED);
991eab5fd59SPaolo Bonzini }
992d35d4cb5SMichael Roth #endif
993d35d4cb5SMichael Roth 
994c216e5adSMichael Roth /* register init/cleanup routines for stateful command groups */
995c216e5adSMichael Roth void ga_command_state_init(GAState *s, GACommandState *cs)
996c216e5adSMichael Roth {
997c216e5adSMichael Roth #if defined(CONFIG_FSFREEZE)
998f22d85e9SMichael Roth     ga_command_state_add(cs, NULL, guest_fsfreeze_cleanup);
999c216e5adSMichael Roth #endif
1000c216e5adSMichael Roth     ga_command_state_add(cs, guest_file_init, NULL);
1001c216e5adSMichael Roth }
1002