xref: /openbmc/qemu/qga/commands-posix.c (revision d35d4cb5)
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>
17c216e5adSMichael Roth #include "qga/guest-agent-core.h"
18c216e5adSMichael Roth #include "qga-qmp-commands.h"
19c216e5adSMichael Roth #include "qerror.h"
20c216e5adSMichael Roth #include "qemu-queue.h"
213424fc9fSMichal Privoznik #include "host-utils.h"
22c216e5adSMichael Roth 
23e72c3f2eSMichael Roth #if defined(__linux__)
24e72c3f2eSMichael Roth #include <mntent.h>
25e72c3f2eSMichael Roth #include <linux/fs.h>
26e72c3f2eSMichael Roth #include <ifaddrs.h>
27e72c3f2eSMichael Roth #include <arpa/inet.h>
28e72c3f2eSMichael Roth #include <sys/socket.h>
29e72c3f2eSMichael Roth #include <net/if.h>
30e72c3f2eSMichael Roth #include <sys/wait.h>
31e72c3f2eSMichael Roth 
32e72c3f2eSMichael Roth #if defined(__linux__) && defined(FIFREEZE)
33e72c3f2eSMichael Roth #define CONFIG_FSFREEZE
34e72c3f2eSMichael Roth #endif
35e72c3f2eSMichael Roth #endif
36e72c3f2eSMichael Roth 
37e72c3f2eSMichael Roth #if defined(__linux__)
38e72c3f2eSMichael Roth /* TODO: use this in place of all post-fork() fclose(std*) callers */
3911d0f125SLuiz Capitulino static void reopen_fd_to_null(int fd)
4011d0f125SLuiz Capitulino {
4111d0f125SLuiz Capitulino     int nullfd;
4211d0f125SLuiz Capitulino 
4311d0f125SLuiz Capitulino     nullfd = open("/dev/null", O_RDWR);
4411d0f125SLuiz Capitulino     if (nullfd < 0) {
4511d0f125SLuiz Capitulino         return;
4611d0f125SLuiz Capitulino     }
4711d0f125SLuiz Capitulino 
4811d0f125SLuiz Capitulino     dup2(nullfd, fd);
4911d0f125SLuiz Capitulino 
5011d0f125SLuiz Capitulino     if (nullfd != fd) {
5111d0f125SLuiz Capitulino         close(nullfd);
5211d0f125SLuiz Capitulino     }
5311d0f125SLuiz Capitulino }
54e72c3f2eSMichael Roth #endif /* defined(__linux__) */
5511d0f125SLuiz Capitulino 
56c216e5adSMichael Roth void qmp_guest_shutdown(bool has_mode, const char *mode, Error **err)
57c216e5adSMichael Roth {
58c216e5adSMichael Roth     int ret;
59c216e5adSMichael Roth     const char *shutdown_flag;
60c216e5adSMichael Roth 
61c216e5adSMichael Roth     slog("guest-shutdown called, mode: %s", mode);
62c216e5adSMichael Roth     if (!has_mode || strcmp(mode, "powerdown") == 0) {
63c216e5adSMichael Roth         shutdown_flag = "-P";
64c216e5adSMichael Roth     } else if (strcmp(mode, "halt") == 0) {
65c216e5adSMichael Roth         shutdown_flag = "-H";
66c216e5adSMichael Roth     } else if (strcmp(mode, "reboot") == 0) {
67c216e5adSMichael Roth         shutdown_flag = "-r";
68c216e5adSMichael Roth     } else {
69c216e5adSMichael Roth         error_set(err, QERR_INVALID_PARAMETER_VALUE, "mode",
70c216e5adSMichael Roth                   "halt|powerdown|reboot");
71c216e5adSMichael Roth         return;
72c216e5adSMichael Roth     }
73c216e5adSMichael Roth 
74c216e5adSMichael Roth     ret = fork();
75c216e5adSMichael Roth     if (ret == 0) {
76c216e5adSMichael Roth         /* child, start the shutdown */
77c216e5adSMichael Roth         setsid();
78c216e5adSMichael Roth         fclose(stdin);
79c216e5adSMichael Roth         fclose(stdout);
80c216e5adSMichael Roth         fclose(stderr);
81c216e5adSMichael Roth 
82c216e5adSMichael Roth         ret = execl("/sbin/shutdown", "shutdown", shutdown_flag, "+0",
83c216e5adSMichael Roth                     "hypervisor initiated shutdown", (char*)NULL);
84c216e5adSMichael Roth         if (ret) {
85c216e5adSMichael Roth             slog("guest-shutdown failed: %s", strerror(errno));
86c216e5adSMichael Roth         }
87c216e5adSMichael Roth         exit(!!ret);
88c216e5adSMichael Roth     } else if (ret < 0) {
89c216e5adSMichael Roth         error_set(err, QERR_UNDEFINED_ERROR);
90c216e5adSMichael Roth     }
91c216e5adSMichael Roth }
92c216e5adSMichael Roth 
93c216e5adSMichael Roth typedef struct GuestFileHandle {
94c216e5adSMichael Roth     uint64_t id;
95c216e5adSMichael Roth     FILE *fh;
96c216e5adSMichael Roth     QTAILQ_ENTRY(GuestFileHandle) next;
97c216e5adSMichael Roth } GuestFileHandle;
98c216e5adSMichael Roth 
99c216e5adSMichael Roth static struct {
100c216e5adSMichael Roth     QTAILQ_HEAD(, GuestFileHandle) filehandles;
101c216e5adSMichael Roth } guest_file_state;
102c216e5adSMichael Roth 
103c216e5adSMichael Roth static void guest_file_handle_add(FILE *fh)
104c216e5adSMichael Roth {
105c216e5adSMichael Roth     GuestFileHandle *gfh;
106c216e5adSMichael Roth 
107c216e5adSMichael Roth     gfh = g_malloc0(sizeof(GuestFileHandle));
108c216e5adSMichael Roth     gfh->id = fileno(fh);
109c216e5adSMichael Roth     gfh->fh = fh;
110c216e5adSMichael Roth     QTAILQ_INSERT_TAIL(&guest_file_state.filehandles, gfh, next);
111c216e5adSMichael Roth }
112c216e5adSMichael Roth 
113c216e5adSMichael Roth static GuestFileHandle *guest_file_handle_find(int64_t id)
114c216e5adSMichael Roth {
115c216e5adSMichael Roth     GuestFileHandle *gfh;
116c216e5adSMichael Roth 
117c216e5adSMichael Roth     QTAILQ_FOREACH(gfh, &guest_file_state.filehandles, next)
118c216e5adSMichael Roth     {
119c216e5adSMichael Roth         if (gfh->id == id) {
120c216e5adSMichael Roth             return gfh;
121c216e5adSMichael Roth         }
122c216e5adSMichael Roth     }
123c216e5adSMichael Roth 
124c216e5adSMichael Roth     return NULL;
125c216e5adSMichael Roth }
126c216e5adSMichael Roth 
127c216e5adSMichael Roth int64_t qmp_guest_file_open(const char *path, bool has_mode, const char *mode, Error **err)
128c216e5adSMichael Roth {
129c216e5adSMichael Roth     FILE *fh;
130c216e5adSMichael Roth     int fd;
131c216e5adSMichael Roth     int64_t ret = -1;
132c216e5adSMichael Roth 
133c216e5adSMichael Roth     if (!has_mode) {
134c216e5adSMichael Roth         mode = "r";
135c216e5adSMichael Roth     }
136c216e5adSMichael Roth     slog("guest-file-open called, filepath: %s, mode: %s", path, mode);
137c216e5adSMichael Roth     fh = fopen(path, mode);
138c216e5adSMichael Roth     if (!fh) {
139c216e5adSMichael Roth         error_set(err, QERR_OPEN_FILE_FAILED, path);
140c216e5adSMichael Roth         return -1;
141c216e5adSMichael Roth     }
142c216e5adSMichael Roth 
143c216e5adSMichael Roth     /* set fd non-blocking to avoid common use cases (like reading from a
144c216e5adSMichael Roth      * named pipe) from hanging the agent
145c216e5adSMichael Roth      */
146c216e5adSMichael Roth     fd = fileno(fh);
147c216e5adSMichael Roth     ret = fcntl(fd, F_GETFL);
148c216e5adSMichael Roth     ret = fcntl(fd, F_SETFL, ret | O_NONBLOCK);
149c216e5adSMichael Roth     if (ret == -1) {
150c216e5adSMichael Roth         error_set(err, QERR_QGA_COMMAND_FAILED, "fcntl() failed");
151c216e5adSMichael Roth         fclose(fh);
152c216e5adSMichael Roth         return -1;
153c216e5adSMichael Roth     }
154c216e5adSMichael Roth 
155c216e5adSMichael Roth     guest_file_handle_add(fh);
156c216e5adSMichael Roth     slog("guest-file-open, handle: %d", fd);
157c216e5adSMichael Roth     return fd;
158c216e5adSMichael Roth }
159c216e5adSMichael Roth 
160c216e5adSMichael Roth void qmp_guest_file_close(int64_t handle, Error **err)
161c216e5adSMichael Roth {
162c216e5adSMichael Roth     GuestFileHandle *gfh = guest_file_handle_find(handle);
163c216e5adSMichael Roth     int ret;
164c216e5adSMichael Roth 
165c216e5adSMichael Roth     slog("guest-file-close called, handle: %ld", handle);
166c216e5adSMichael Roth     if (!gfh) {
167c216e5adSMichael Roth         error_set(err, QERR_FD_NOT_FOUND, "handle");
168c216e5adSMichael Roth         return;
169c216e5adSMichael Roth     }
170c216e5adSMichael Roth 
171c216e5adSMichael Roth     ret = fclose(gfh->fh);
172c216e5adSMichael Roth     if (ret == -1) {
173c216e5adSMichael Roth         error_set(err, QERR_QGA_COMMAND_FAILED, "fclose() failed");
174c216e5adSMichael Roth         return;
175c216e5adSMichael Roth     }
176c216e5adSMichael Roth 
177c216e5adSMichael Roth     QTAILQ_REMOVE(&guest_file_state.filehandles, gfh, next);
178c216e5adSMichael Roth     g_free(gfh);
179c216e5adSMichael Roth }
180c216e5adSMichael Roth 
181c216e5adSMichael Roth struct GuestFileRead *qmp_guest_file_read(int64_t handle, bool has_count,
182c216e5adSMichael Roth                                           int64_t count, Error **err)
183c216e5adSMichael Roth {
184c216e5adSMichael Roth     GuestFileHandle *gfh = guest_file_handle_find(handle);
185c216e5adSMichael Roth     GuestFileRead *read_data = NULL;
186c216e5adSMichael Roth     guchar *buf;
187c216e5adSMichael Roth     FILE *fh;
188c216e5adSMichael Roth     size_t read_count;
189c216e5adSMichael Roth 
190c216e5adSMichael Roth     if (!gfh) {
191c216e5adSMichael Roth         error_set(err, QERR_FD_NOT_FOUND, "handle");
192c216e5adSMichael Roth         return NULL;
193c216e5adSMichael Roth     }
194c216e5adSMichael Roth 
195c216e5adSMichael Roth     if (!has_count) {
196c216e5adSMichael Roth         count = QGA_READ_COUNT_DEFAULT;
197c216e5adSMichael Roth     } else if (count < 0) {
198c216e5adSMichael Roth         error_set(err, QERR_INVALID_PARAMETER, "count");
199c216e5adSMichael Roth         return NULL;
200c216e5adSMichael Roth     }
201c216e5adSMichael Roth 
202c216e5adSMichael Roth     fh = gfh->fh;
203c216e5adSMichael Roth     buf = g_malloc0(count+1);
204c216e5adSMichael Roth     read_count = fread(buf, 1, count, fh);
205c216e5adSMichael Roth     if (ferror(fh)) {
206c216e5adSMichael Roth         slog("guest-file-read failed, handle: %ld", handle);
207c216e5adSMichael Roth         error_set(err, QERR_QGA_COMMAND_FAILED, "fread() failed");
208c216e5adSMichael Roth     } else {
209c216e5adSMichael Roth         buf[read_count] = 0;
210c216e5adSMichael Roth         read_data = g_malloc0(sizeof(GuestFileRead));
211c216e5adSMichael Roth         read_data->count = read_count;
212c216e5adSMichael Roth         read_data->eof = feof(fh);
213c216e5adSMichael Roth         if (read_count) {
214c216e5adSMichael Roth             read_data->buf_b64 = g_base64_encode(buf, read_count);
215c216e5adSMichael Roth         }
216c216e5adSMichael Roth     }
217c216e5adSMichael Roth     g_free(buf);
218c216e5adSMichael Roth     clearerr(fh);
219c216e5adSMichael Roth 
220c216e5adSMichael Roth     return read_data;
221c216e5adSMichael Roth }
222c216e5adSMichael Roth 
223c216e5adSMichael Roth GuestFileWrite *qmp_guest_file_write(int64_t handle, const char *buf_b64,
224c216e5adSMichael Roth                                      bool has_count, int64_t count, Error **err)
225c216e5adSMichael Roth {
226c216e5adSMichael Roth     GuestFileWrite *write_data = NULL;
227c216e5adSMichael Roth     guchar *buf;
228c216e5adSMichael Roth     gsize buf_len;
229c216e5adSMichael Roth     int write_count;
230c216e5adSMichael Roth     GuestFileHandle *gfh = guest_file_handle_find(handle);
231c216e5adSMichael Roth     FILE *fh;
232c216e5adSMichael Roth 
233c216e5adSMichael Roth     if (!gfh) {
234c216e5adSMichael Roth         error_set(err, QERR_FD_NOT_FOUND, "handle");
235c216e5adSMichael Roth         return NULL;
236c216e5adSMichael Roth     }
237c216e5adSMichael Roth 
238c216e5adSMichael Roth     fh = gfh->fh;
239c216e5adSMichael Roth     buf = g_base64_decode(buf_b64, &buf_len);
240c216e5adSMichael Roth 
241c216e5adSMichael Roth     if (!has_count) {
242c216e5adSMichael Roth         count = buf_len;
243c216e5adSMichael Roth     } else if (count < 0 || count > buf_len) {
244c216e5adSMichael Roth         g_free(buf);
245c216e5adSMichael Roth         error_set(err, QERR_INVALID_PARAMETER, "count");
246c216e5adSMichael Roth         return NULL;
247c216e5adSMichael Roth     }
248c216e5adSMichael Roth 
249c216e5adSMichael Roth     write_count = fwrite(buf, 1, count, fh);
250c216e5adSMichael Roth     if (ferror(fh)) {
251c216e5adSMichael Roth         slog("guest-file-write failed, handle: %ld", handle);
252c216e5adSMichael Roth         error_set(err, QERR_QGA_COMMAND_FAILED, "fwrite() error");
253c216e5adSMichael Roth     } else {
254c216e5adSMichael Roth         write_data = g_malloc0(sizeof(GuestFileWrite));
255c216e5adSMichael Roth         write_data->count = write_count;
256c216e5adSMichael Roth         write_data->eof = feof(fh);
257c216e5adSMichael Roth     }
258c216e5adSMichael Roth     g_free(buf);
259c216e5adSMichael Roth     clearerr(fh);
260c216e5adSMichael Roth 
261c216e5adSMichael Roth     return write_data;
262c216e5adSMichael Roth }
263c216e5adSMichael Roth 
264c216e5adSMichael Roth struct GuestFileSeek *qmp_guest_file_seek(int64_t handle, int64_t offset,
265c216e5adSMichael Roth                                           int64_t whence, Error **err)
266c216e5adSMichael Roth {
267c216e5adSMichael Roth     GuestFileHandle *gfh = guest_file_handle_find(handle);
268c216e5adSMichael Roth     GuestFileSeek *seek_data = NULL;
269c216e5adSMichael Roth     FILE *fh;
270c216e5adSMichael Roth     int ret;
271c216e5adSMichael Roth 
272c216e5adSMichael Roth     if (!gfh) {
273c216e5adSMichael Roth         error_set(err, QERR_FD_NOT_FOUND, "handle");
274c216e5adSMichael Roth         return NULL;
275c216e5adSMichael Roth     }
276c216e5adSMichael Roth 
277c216e5adSMichael Roth     fh = gfh->fh;
278c216e5adSMichael Roth     ret = fseek(fh, offset, whence);
279c216e5adSMichael Roth     if (ret == -1) {
280c216e5adSMichael Roth         error_set(err, QERR_QGA_COMMAND_FAILED, strerror(errno));
281c216e5adSMichael Roth     } else {
282c216e5adSMichael Roth         seek_data = g_malloc0(sizeof(GuestFileRead));
283c216e5adSMichael Roth         seek_data->position = ftell(fh);
284c216e5adSMichael Roth         seek_data->eof = feof(fh);
285c216e5adSMichael Roth     }
286c216e5adSMichael Roth     clearerr(fh);
287c216e5adSMichael Roth 
288c216e5adSMichael Roth     return seek_data;
289c216e5adSMichael Roth }
290c216e5adSMichael Roth 
291c216e5adSMichael Roth void qmp_guest_file_flush(int64_t handle, Error **err)
292c216e5adSMichael Roth {
293c216e5adSMichael Roth     GuestFileHandle *gfh = guest_file_handle_find(handle);
294c216e5adSMichael Roth     FILE *fh;
295c216e5adSMichael Roth     int ret;
296c216e5adSMichael Roth 
297c216e5adSMichael Roth     if (!gfh) {
298c216e5adSMichael Roth         error_set(err, QERR_FD_NOT_FOUND, "handle");
299c216e5adSMichael Roth         return;
300c216e5adSMichael Roth     }
301c216e5adSMichael Roth 
302c216e5adSMichael Roth     fh = gfh->fh;
303c216e5adSMichael Roth     ret = fflush(fh);
304c216e5adSMichael Roth     if (ret == EOF) {
305c216e5adSMichael Roth         error_set(err, QERR_QGA_COMMAND_FAILED, strerror(errno));
306c216e5adSMichael Roth     }
307c216e5adSMichael Roth }
308c216e5adSMichael Roth 
309c216e5adSMichael Roth static void guest_file_init(void)
310c216e5adSMichael Roth {
311c216e5adSMichael Roth     QTAILQ_INIT(&guest_file_state.filehandles);
312c216e5adSMichael Roth }
313c216e5adSMichael Roth 
314e72c3f2eSMichael Roth /* linux-specific implementations. avoid this if at all possible. */
315e72c3f2eSMichael Roth #if defined(__linux__)
316e72c3f2eSMichael Roth 
317c216e5adSMichael Roth #if defined(CONFIG_FSFREEZE)
318e72c3f2eSMichael Roth 
319c216e5adSMichael Roth static void disable_logging(void)
320c216e5adSMichael Roth {
321c216e5adSMichael Roth     ga_disable_logging(ga_state);
322c216e5adSMichael Roth }
323c216e5adSMichael Roth 
324c216e5adSMichael Roth static void enable_logging(void)
325c216e5adSMichael Roth {
326c216e5adSMichael Roth     ga_enable_logging(ga_state);
327c216e5adSMichael Roth }
328c216e5adSMichael Roth 
329c216e5adSMichael Roth typedef struct GuestFsfreezeMount {
330c216e5adSMichael Roth     char *dirname;
331c216e5adSMichael Roth     char *devtype;
332c216e5adSMichael Roth     QTAILQ_ENTRY(GuestFsfreezeMount) next;
333c216e5adSMichael Roth } GuestFsfreezeMount;
334c216e5adSMichael Roth 
335c216e5adSMichael Roth struct {
336c216e5adSMichael Roth     GuestFsfreezeStatus status;
337c216e5adSMichael Roth     QTAILQ_HEAD(, GuestFsfreezeMount) mount_list;
338c216e5adSMichael Roth } guest_fsfreeze_state;
339c216e5adSMichael Roth 
340c216e5adSMichael Roth /*
341c216e5adSMichael Roth  * Walk the mount table and build a list of local file systems
342c216e5adSMichael Roth  */
343c216e5adSMichael Roth static int guest_fsfreeze_build_mount_list(void)
344c216e5adSMichael Roth {
345c216e5adSMichael Roth     struct mntent *ment;
346c216e5adSMichael Roth     GuestFsfreezeMount *mount, *temp;
347c216e5adSMichael Roth     char const *mtab = MOUNTED;
348c216e5adSMichael Roth     FILE *fp;
349c216e5adSMichael Roth 
350c216e5adSMichael Roth     QTAILQ_FOREACH_SAFE(mount, &guest_fsfreeze_state.mount_list, next, temp) {
351c216e5adSMichael Roth         QTAILQ_REMOVE(&guest_fsfreeze_state.mount_list, mount, next);
352c216e5adSMichael Roth         g_free(mount->dirname);
353c216e5adSMichael Roth         g_free(mount->devtype);
354c216e5adSMichael Roth         g_free(mount);
355c216e5adSMichael Roth     }
356c216e5adSMichael Roth 
357c216e5adSMichael Roth     fp = setmntent(mtab, "r");
358c216e5adSMichael Roth     if (!fp) {
359c216e5adSMichael Roth         g_warning("fsfreeze: unable to read mtab");
360c216e5adSMichael Roth         return -1;
361c216e5adSMichael Roth     }
362c216e5adSMichael Roth 
363c216e5adSMichael Roth     while ((ment = getmntent(fp))) {
364c216e5adSMichael Roth         /*
365c216e5adSMichael Roth          * An entry which device name doesn't start with a '/' is
366c216e5adSMichael Roth          * either a dummy file system or a network file system.
367c216e5adSMichael Roth          * Add special handling for smbfs and cifs as is done by
368c216e5adSMichael Roth          * coreutils as well.
369c216e5adSMichael Roth          */
370c216e5adSMichael Roth         if ((ment->mnt_fsname[0] != '/') ||
371c216e5adSMichael Roth             (strcmp(ment->mnt_type, "smbfs") == 0) ||
372c216e5adSMichael Roth             (strcmp(ment->mnt_type, "cifs") == 0)) {
373c216e5adSMichael Roth             continue;
374c216e5adSMichael Roth         }
375c216e5adSMichael Roth 
376c216e5adSMichael Roth         mount = g_malloc0(sizeof(GuestFsfreezeMount));
377c216e5adSMichael Roth         mount->dirname = g_strdup(ment->mnt_dir);
378c216e5adSMichael Roth         mount->devtype = g_strdup(ment->mnt_type);
379c216e5adSMichael Roth 
380c216e5adSMichael Roth         QTAILQ_INSERT_TAIL(&guest_fsfreeze_state.mount_list, mount, next);
381c216e5adSMichael Roth     }
382c216e5adSMichael Roth 
383c216e5adSMichael Roth     endmntent(fp);
384c216e5adSMichael Roth 
385c216e5adSMichael Roth     return 0;
386c216e5adSMichael Roth }
387c216e5adSMichael Roth 
388c216e5adSMichael Roth /*
389c216e5adSMichael Roth  * Return status of freeze/thaw
390c216e5adSMichael Roth  */
391c216e5adSMichael Roth GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **err)
392c216e5adSMichael Roth {
393c216e5adSMichael Roth     return guest_fsfreeze_state.status;
394c216e5adSMichael Roth }
395c216e5adSMichael Roth 
396c216e5adSMichael Roth /*
397c216e5adSMichael Roth  * Walk list of mounted file systems in the guest, and freeze the ones which
398c216e5adSMichael Roth  * are real local file systems.
399c216e5adSMichael Roth  */
400c216e5adSMichael Roth int64_t qmp_guest_fsfreeze_freeze(Error **err)
401c216e5adSMichael Roth {
402c216e5adSMichael Roth     int ret = 0, i = 0;
403c216e5adSMichael Roth     struct GuestFsfreezeMount *mount, *temp;
404c216e5adSMichael Roth     int fd;
405c216e5adSMichael Roth     char err_msg[512];
406c216e5adSMichael Roth 
407c216e5adSMichael Roth     slog("guest-fsfreeze called");
408c216e5adSMichael Roth 
409c216e5adSMichael Roth     if (guest_fsfreeze_state.status == GUEST_FSFREEZE_STATUS_FROZEN) {
410c216e5adSMichael Roth         return 0;
411c216e5adSMichael Roth     }
412c216e5adSMichael Roth 
413c216e5adSMichael Roth     ret = guest_fsfreeze_build_mount_list();
414c216e5adSMichael Roth     if (ret < 0) {
415c216e5adSMichael Roth         return ret;
416c216e5adSMichael Roth     }
417c216e5adSMichael Roth 
418c216e5adSMichael Roth     /* cannot risk guest agent blocking itself on a write in this state */
419c216e5adSMichael Roth     disable_logging();
420c216e5adSMichael Roth 
421c216e5adSMichael Roth     QTAILQ_FOREACH_SAFE(mount, &guest_fsfreeze_state.mount_list, next, temp) {
422c216e5adSMichael Roth         fd = qemu_open(mount->dirname, O_RDONLY);
423c216e5adSMichael Roth         if (fd == -1) {
424c216e5adSMichael Roth             sprintf(err_msg, "failed to open %s, %s", mount->dirname, strerror(errno));
425c216e5adSMichael Roth             error_set(err, QERR_QGA_COMMAND_FAILED, err_msg);
426c216e5adSMichael Roth             goto error;
427c216e5adSMichael Roth         }
428c216e5adSMichael Roth 
429c216e5adSMichael Roth         /* we try to cull filesytems we know won't work in advance, but other
430c216e5adSMichael Roth          * filesytems may not implement fsfreeze for less obvious reasons.
431c216e5adSMichael Roth          * these will report EOPNOTSUPP, so we simply ignore them. when
432c216e5adSMichael Roth          * thawing, these filesystems will return an EINVAL instead, due to
433c216e5adSMichael Roth          * not being in a frozen state. Other filesystem-specific
434c216e5adSMichael Roth          * errors may result in EINVAL, however, so the user should check the
435c216e5adSMichael Roth          * number * of filesystems returned here against those returned by the
436c216e5adSMichael Roth          * thaw operation to determine whether everything completed
437c216e5adSMichael Roth          * successfully
438c216e5adSMichael Roth          */
439c216e5adSMichael Roth         ret = ioctl(fd, FIFREEZE);
440c216e5adSMichael Roth         if (ret < 0 && errno != EOPNOTSUPP) {
441c216e5adSMichael Roth             sprintf(err_msg, "failed to freeze %s, %s", mount->dirname, strerror(errno));
442c216e5adSMichael Roth             error_set(err, QERR_QGA_COMMAND_FAILED, err_msg);
443c216e5adSMichael Roth             close(fd);
444c216e5adSMichael Roth             goto error;
445c216e5adSMichael Roth         }
446c216e5adSMichael Roth         close(fd);
447c216e5adSMichael Roth 
448c216e5adSMichael Roth         i++;
449c216e5adSMichael Roth     }
450c216e5adSMichael Roth 
451c216e5adSMichael Roth     guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_FROZEN;
452c216e5adSMichael Roth     return i;
453c216e5adSMichael Roth 
454c216e5adSMichael Roth error:
455c216e5adSMichael Roth     if (i > 0) {
456c216e5adSMichael Roth         qmp_guest_fsfreeze_thaw(NULL);
457c216e5adSMichael Roth     }
458c216e5adSMichael Roth     return 0;
459c216e5adSMichael Roth }
460c216e5adSMichael Roth 
461c216e5adSMichael Roth /*
462c216e5adSMichael Roth  * Walk list of frozen file systems in the guest, and thaw them.
463c216e5adSMichael Roth  */
464c216e5adSMichael Roth int64_t qmp_guest_fsfreeze_thaw(Error **err)
465c216e5adSMichael Roth {
466c216e5adSMichael Roth     int ret;
467c216e5adSMichael Roth     GuestFsfreezeMount *mount, *temp;
468c216e5adSMichael Roth     int fd, i = 0;
469c216e5adSMichael Roth     bool has_error = false;
470c216e5adSMichael Roth 
471c216e5adSMichael Roth     QTAILQ_FOREACH_SAFE(mount, &guest_fsfreeze_state.mount_list, next, temp) {
472c216e5adSMichael Roth         fd = qemu_open(mount->dirname, O_RDONLY);
473c216e5adSMichael Roth         if (fd == -1) {
474c216e5adSMichael Roth             has_error = true;
475c216e5adSMichael Roth             continue;
476c216e5adSMichael Roth         }
477c216e5adSMichael Roth         ret = ioctl(fd, FITHAW);
478c216e5adSMichael Roth         if (ret < 0 && errno != EOPNOTSUPP && errno != EINVAL) {
479c216e5adSMichael Roth             has_error = true;
480c216e5adSMichael Roth             close(fd);
481c216e5adSMichael Roth             continue;
482c216e5adSMichael Roth         }
483c216e5adSMichael Roth         close(fd);
484c216e5adSMichael Roth         i++;
485c216e5adSMichael Roth     }
486c216e5adSMichael Roth 
487c216e5adSMichael Roth     if (has_error) {
488c216e5adSMichael Roth         guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_ERROR;
489c216e5adSMichael Roth     } else {
490c216e5adSMichael Roth         guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_THAWED;
491c216e5adSMichael Roth     }
492c216e5adSMichael Roth     enable_logging();
493c216e5adSMichael Roth     return i;
494c216e5adSMichael Roth }
495c216e5adSMichael Roth 
496c216e5adSMichael Roth static void guest_fsfreeze_init(void)
497c216e5adSMichael Roth {
498c216e5adSMichael Roth     guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_THAWED;
499c216e5adSMichael Roth     QTAILQ_INIT(&guest_fsfreeze_state.mount_list);
500c216e5adSMichael Roth }
501c216e5adSMichael Roth 
502c216e5adSMichael Roth static void guest_fsfreeze_cleanup(void)
503c216e5adSMichael Roth {
504c216e5adSMichael Roth     int64_t ret;
505c216e5adSMichael Roth     Error *err = NULL;
506c216e5adSMichael Roth 
507c216e5adSMichael Roth     if (guest_fsfreeze_state.status == GUEST_FSFREEZE_STATUS_FROZEN) {
508c216e5adSMichael Roth         ret = qmp_guest_fsfreeze_thaw(&err);
509c216e5adSMichael Roth         if (ret < 0 || err) {
510c216e5adSMichael Roth             slog("failed to clean up frozen filesystems");
511c216e5adSMichael Roth         }
512c216e5adSMichael Roth     }
513c216e5adSMichael Roth }
514e72c3f2eSMichael Roth #endif /* CONFIG_FSFREEZE */
515c216e5adSMichael Roth 
51611d0f125SLuiz Capitulino #define LINUX_SYS_STATE_FILE "/sys/power/state"
51711d0f125SLuiz Capitulino #define SUSPEND_SUPPORTED 0
51811d0f125SLuiz Capitulino #define SUSPEND_NOT_SUPPORTED 1
51911d0f125SLuiz Capitulino 
52011d0f125SLuiz Capitulino /**
52111d0f125SLuiz Capitulino  * This function forks twice and the information about the mode support
52211d0f125SLuiz Capitulino  * status is passed to the qemu-ga process via a pipe.
52311d0f125SLuiz Capitulino  *
52411d0f125SLuiz Capitulino  * This approach allows us to keep the way we reap terminated children
52511d0f125SLuiz Capitulino  * in qemu-ga quite simple.
52611d0f125SLuiz Capitulino  */
52711d0f125SLuiz Capitulino static void bios_supports_mode(const char *pmutils_bin, const char *pmutils_arg,
52811d0f125SLuiz Capitulino                                const char *sysfile_str, Error **err)
52911d0f125SLuiz Capitulino {
53011d0f125SLuiz Capitulino     pid_t pid;
53111d0f125SLuiz Capitulino     ssize_t ret;
53211d0f125SLuiz Capitulino     char *pmutils_path;
53311d0f125SLuiz Capitulino     int status, pipefds[2];
53411d0f125SLuiz Capitulino 
53511d0f125SLuiz Capitulino     if (pipe(pipefds) < 0) {
53611d0f125SLuiz Capitulino         error_set(err, QERR_UNDEFINED_ERROR);
53711d0f125SLuiz Capitulino         return;
53811d0f125SLuiz Capitulino     }
53911d0f125SLuiz Capitulino 
54011d0f125SLuiz Capitulino     pmutils_path = g_find_program_in_path(pmutils_bin);
54111d0f125SLuiz Capitulino 
54211d0f125SLuiz Capitulino     pid = fork();
54311d0f125SLuiz Capitulino     if (!pid) {
54411d0f125SLuiz Capitulino         struct sigaction act;
54511d0f125SLuiz Capitulino 
54611d0f125SLuiz Capitulino         memset(&act, 0, sizeof(act));
54711d0f125SLuiz Capitulino         act.sa_handler = SIG_DFL;
54811d0f125SLuiz Capitulino         sigaction(SIGCHLD, &act, NULL);
54911d0f125SLuiz Capitulino 
55011d0f125SLuiz Capitulino         setsid();
55111d0f125SLuiz Capitulino         close(pipefds[0]);
55211d0f125SLuiz Capitulino         reopen_fd_to_null(0);
55311d0f125SLuiz Capitulino         reopen_fd_to_null(1);
55411d0f125SLuiz Capitulino         reopen_fd_to_null(2);
55511d0f125SLuiz Capitulino 
55611d0f125SLuiz Capitulino         pid = fork();
55711d0f125SLuiz Capitulino         if (!pid) {
55811d0f125SLuiz Capitulino             int fd;
55911d0f125SLuiz Capitulino             char buf[32]; /* hopefully big enough */
56011d0f125SLuiz Capitulino 
56111d0f125SLuiz Capitulino             if (pmutils_path) {
56211d0f125SLuiz Capitulino                 execle(pmutils_path, pmutils_bin, pmutils_arg, NULL, environ);
56311d0f125SLuiz Capitulino             }
56411d0f125SLuiz Capitulino 
56511d0f125SLuiz Capitulino             /*
56611d0f125SLuiz Capitulino              * If we get here either pm-utils is not installed or execle() has
56711d0f125SLuiz Capitulino              * failed. Let's try the manual method if the caller wants it.
56811d0f125SLuiz Capitulino              */
56911d0f125SLuiz Capitulino 
57011d0f125SLuiz Capitulino             if (!sysfile_str) {
57111d0f125SLuiz Capitulino                 _exit(SUSPEND_NOT_SUPPORTED);
57211d0f125SLuiz Capitulino             }
57311d0f125SLuiz Capitulino 
57411d0f125SLuiz Capitulino             fd = open(LINUX_SYS_STATE_FILE, O_RDONLY);
57511d0f125SLuiz Capitulino             if (fd < 0) {
57611d0f125SLuiz Capitulino                 _exit(SUSPEND_NOT_SUPPORTED);
57711d0f125SLuiz Capitulino             }
57811d0f125SLuiz Capitulino 
57911d0f125SLuiz Capitulino             ret = read(fd, buf, sizeof(buf)-1);
58011d0f125SLuiz Capitulino             if (ret <= 0) {
58111d0f125SLuiz Capitulino                 _exit(SUSPEND_NOT_SUPPORTED);
58211d0f125SLuiz Capitulino             }
58311d0f125SLuiz Capitulino             buf[ret] = '\0';
58411d0f125SLuiz Capitulino 
58511d0f125SLuiz Capitulino             if (strstr(buf, sysfile_str)) {
58611d0f125SLuiz Capitulino                 _exit(SUSPEND_SUPPORTED);
58711d0f125SLuiz Capitulino             }
58811d0f125SLuiz Capitulino 
58911d0f125SLuiz Capitulino             _exit(SUSPEND_NOT_SUPPORTED);
59011d0f125SLuiz Capitulino         }
59111d0f125SLuiz Capitulino 
59211d0f125SLuiz Capitulino         if (pid > 0) {
59311d0f125SLuiz Capitulino             wait(&status);
59411d0f125SLuiz Capitulino         } else {
59511d0f125SLuiz Capitulino             status = SUSPEND_NOT_SUPPORTED;
59611d0f125SLuiz Capitulino         }
59711d0f125SLuiz Capitulino 
59811d0f125SLuiz Capitulino         ret = write(pipefds[1], &status, sizeof(status));
59911d0f125SLuiz Capitulino         if (ret != sizeof(status)) {
60011d0f125SLuiz Capitulino             _exit(EXIT_FAILURE);
60111d0f125SLuiz Capitulino         }
60211d0f125SLuiz Capitulino 
60311d0f125SLuiz Capitulino         _exit(EXIT_SUCCESS);
60411d0f125SLuiz Capitulino     }
60511d0f125SLuiz Capitulino 
60611d0f125SLuiz Capitulino     close(pipefds[1]);
60711d0f125SLuiz Capitulino     g_free(pmutils_path);
60811d0f125SLuiz Capitulino 
60911d0f125SLuiz Capitulino     if (pid < 0) {
61011d0f125SLuiz Capitulino         error_set(err, QERR_UNDEFINED_ERROR);
61111d0f125SLuiz Capitulino         goto out;
61211d0f125SLuiz Capitulino     }
61311d0f125SLuiz Capitulino 
61411d0f125SLuiz Capitulino     ret = read(pipefds[0], &status, sizeof(status));
61511d0f125SLuiz Capitulino     if (ret == sizeof(status) && WIFEXITED(status) &&
61611d0f125SLuiz Capitulino         WEXITSTATUS(status) == SUSPEND_SUPPORTED) {
61711d0f125SLuiz Capitulino             goto out;
61811d0f125SLuiz Capitulino     }
61911d0f125SLuiz Capitulino 
62011d0f125SLuiz Capitulino     error_set(err, QERR_UNSUPPORTED);
62111d0f125SLuiz Capitulino 
62211d0f125SLuiz Capitulino out:
62311d0f125SLuiz Capitulino     close(pipefds[0]);
62411d0f125SLuiz Capitulino }
62511d0f125SLuiz Capitulino 
62611d0f125SLuiz Capitulino static void guest_suspend(const char *pmutils_bin, const char *sysfile_str,
62711d0f125SLuiz Capitulino                           Error **err)
62811d0f125SLuiz Capitulino {
62911d0f125SLuiz Capitulino     pid_t pid;
63011d0f125SLuiz Capitulino     char *pmutils_path;
63111d0f125SLuiz Capitulino 
63211d0f125SLuiz Capitulino     pmutils_path = g_find_program_in_path(pmutils_bin);
63311d0f125SLuiz Capitulino 
63411d0f125SLuiz Capitulino     pid = fork();
63511d0f125SLuiz Capitulino     if (pid == 0) {
63611d0f125SLuiz Capitulino         /* child */
63711d0f125SLuiz Capitulino         int fd;
63811d0f125SLuiz Capitulino 
63911d0f125SLuiz Capitulino         setsid();
64011d0f125SLuiz Capitulino         reopen_fd_to_null(0);
64111d0f125SLuiz Capitulino         reopen_fd_to_null(1);
64211d0f125SLuiz Capitulino         reopen_fd_to_null(2);
64311d0f125SLuiz Capitulino 
64411d0f125SLuiz Capitulino         if (pmutils_path) {
64511d0f125SLuiz Capitulino             execle(pmutils_path, pmutils_bin, NULL, environ);
64611d0f125SLuiz Capitulino         }
64711d0f125SLuiz Capitulino 
64811d0f125SLuiz Capitulino         /*
64911d0f125SLuiz Capitulino          * If we get here either pm-utils is not installed or execle() has
65011d0f125SLuiz Capitulino          * failed. Let's try the manual method if the caller wants it.
65111d0f125SLuiz Capitulino          */
65211d0f125SLuiz Capitulino 
65311d0f125SLuiz Capitulino         if (!sysfile_str) {
65411d0f125SLuiz Capitulino             _exit(EXIT_FAILURE);
65511d0f125SLuiz Capitulino         }
65611d0f125SLuiz Capitulino 
65711d0f125SLuiz Capitulino         fd = open(LINUX_SYS_STATE_FILE, O_WRONLY);
65811d0f125SLuiz Capitulino         if (fd < 0) {
65911d0f125SLuiz Capitulino             _exit(EXIT_FAILURE);
66011d0f125SLuiz Capitulino         }
66111d0f125SLuiz Capitulino 
66211d0f125SLuiz Capitulino         if (write(fd, sysfile_str, strlen(sysfile_str)) < 0) {
66311d0f125SLuiz Capitulino             _exit(EXIT_FAILURE);
66411d0f125SLuiz Capitulino         }
66511d0f125SLuiz Capitulino 
66611d0f125SLuiz Capitulino         _exit(EXIT_SUCCESS);
66711d0f125SLuiz Capitulino     }
66811d0f125SLuiz Capitulino 
66911d0f125SLuiz Capitulino     g_free(pmutils_path);
67011d0f125SLuiz Capitulino 
67111d0f125SLuiz Capitulino     if (pid < 0) {
67211d0f125SLuiz Capitulino         error_set(err, QERR_UNDEFINED_ERROR);
67311d0f125SLuiz Capitulino         return;
67411d0f125SLuiz Capitulino     }
67511d0f125SLuiz Capitulino }
67611d0f125SLuiz Capitulino 
67711d0f125SLuiz Capitulino void qmp_guest_suspend_disk(Error **err)
67811d0f125SLuiz Capitulino {
67911d0f125SLuiz Capitulino     bios_supports_mode("pm-is-supported", "--hibernate", "disk", err);
68011d0f125SLuiz Capitulino     if (error_is_set(err)) {
68111d0f125SLuiz Capitulino         return;
68211d0f125SLuiz Capitulino     }
68311d0f125SLuiz Capitulino 
68411d0f125SLuiz Capitulino     guest_suspend("pm-hibernate", "disk", err);
68511d0f125SLuiz Capitulino }
68611d0f125SLuiz Capitulino 
687fbf42210SLuiz Capitulino void qmp_guest_suspend_ram(Error **err)
688fbf42210SLuiz Capitulino {
689fbf42210SLuiz Capitulino     bios_supports_mode("pm-is-supported", "--suspend", "mem", err);
690fbf42210SLuiz Capitulino     if (error_is_set(err)) {
691fbf42210SLuiz Capitulino         return;
692fbf42210SLuiz Capitulino     }
693fbf42210SLuiz Capitulino 
694fbf42210SLuiz Capitulino     guest_suspend("pm-suspend", "mem", err);
695fbf42210SLuiz Capitulino }
696fbf42210SLuiz Capitulino 
69795f4f404SLuiz Capitulino void qmp_guest_suspend_hybrid(Error **err)
69895f4f404SLuiz Capitulino {
69995f4f404SLuiz Capitulino     bios_supports_mode("pm-is-supported", "--suspend-hybrid", NULL, err);
70095f4f404SLuiz Capitulino     if (error_is_set(err)) {
70195f4f404SLuiz Capitulino         return;
70295f4f404SLuiz Capitulino     }
70395f4f404SLuiz Capitulino 
70495f4f404SLuiz Capitulino     guest_suspend("pm-suspend-hybrid", NULL, err);
70595f4f404SLuiz Capitulino }
70695f4f404SLuiz Capitulino 
7073424fc9fSMichal Privoznik static GuestNetworkInterfaceList *
7083424fc9fSMichal Privoznik guest_find_interface(GuestNetworkInterfaceList *head,
7093424fc9fSMichal Privoznik                      const char *name)
7103424fc9fSMichal Privoznik {
7113424fc9fSMichal Privoznik     for (; head; head = head->next) {
7123424fc9fSMichal Privoznik         if (strcmp(head->value->name, name) == 0) {
7133424fc9fSMichal Privoznik             break;
7143424fc9fSMichal Privoznik         }
7153424fc9fSMichal Privoznik     }
7163424fc9fSMichal Privoznik 
7173424fc9fSMichal Privoznik     return head;
7183424fc9fSMichal Privoznik }
7193424fc9fSMichal Privoznik 
7203424fc9fSMichal Privoznik /*
7213424fc9fSMichal Privoznik  * Build information about guest interfaces
7223424fc9fSMichal Privoznik  */
7233424fc9fSMichal Privoznik GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp)
7243424fc9fSMichal Privoznik {
7253424fc9fSMichal Privoznik     GuestNetworkInterfaceList *head = NULL, *cur_item = NULL;
7263424fc9fSMichal Privoznik     struct ifaddrs *ifap, *ifa;
7273424fc9fSMichal Privoznik     char err_msg[512];
7283424fc9fSMichal Privoznik 
7293424fc9fSMichal Privoznik     if (getifaddrs(&ifap) < 0) {
7303424fc9fSMichal Privoznik         snprintf(err_msg, sizeof(err_msg),
7313424fc9fSMichal Privoznik                  "getifaddrs failed: %s", strerror(errno));
7323424fc9fSMichal Privoznik         error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg);
7333424fc9fSMichal Privoznik         goto error;
7343424fc9fSMichal Privoznik     }
7353424fc9fSMichal Privoznik 
7363424fc9fSMichal Privoznik     for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
7373424fc9fSMichal Privoznik         GuestNetworkInterfaceList *info;
7383424fc9fSMichal Privoznik         GuestIpAddressList **address_list = NULL, *address_item = NULL;
7393424fc9fSMichal Privoznik         char addr4[INET_ADDRSTRLEN];
7403424fc9fSMichal Privoznik         char addr6[INET6_ADDRSTRLEN];
7413424fc9fSMichal Privoznik         int sock;
7423424fc9fSMichal Privoznik         struct ifreq ifr;
7433424fc9fSMichal Privoznik         unsigned char *mac_addr;
7443424fc9fSMichal Privoznik         void *p;
7453424fc9fSMichal Privoznik 
7463424fc9fSMichal Privoznik         g_debug("Processing %s interface", ifa->ifa_name);
7473424fc9fSMichal Privoznik 
7483424fc9fSMichal Privoznik         info = guest_find_interface(head, ifa->ifa_name);
7493424fc9fSMichal Privoznik 
7503424fc9fSMichal Privoznik         if (!info) {
7513424fc9fSMichal Privoznik             info = g_malloc0(sizeof(*info));
7523424fc9fSMichal Privoznik             info->value = g_malloc0(sizeof(*info->value));
7533424fc9fSMichal Privoznik             info->value->name = g_strdup(ifa->ifa_name);
7543424fc9fSMichal Privoznik 
7553424fc9fSMichal Privoznik             if (!cur_item) {
7563424fc9fSMichal Privoznik                 head = cur_item = info;
7573424fc9fSMichal Privoznik             } else {
7583424fc9fSMichal Privoznik                 cur_item->next = info;
7593424fc9fSMichal Privoznik                 cur_item = info;
7603424fc9fSMichal Privoznik             }
7613424fc9fSMichal Privoznik         }
7623424fc9fSMichal Privoznik 
7633424fc9fSMichal Privoznik         if (!info->value->has_hardware_address &&
7643424fc9fSMichal Privoznik             ifa->ifa_flags & SIOCGIFHWADDR) {
7653424fc9fSMichal Privoznik             /* we haven't obtained HW address yet */
7663424fc9fSMichal Privoznik             sock = socket(PF_INET, SOCK_STREAM, 0);
7673424fc9fSMichal Privoznik             if (sock == -1) {
7683424fc9fSMichal Privoznik                 snprintf(err_msg, sizeof(err_msg),
7693424fc9fSMichal Privoznik                          "failed to create socket: %s", strerror(errno));
7703424fc9fSMichal Privoznik                 error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg);
7713424fc9fSMichal Privoznik                 goto error;
7723424fc9fSMichal Privoznik             }
7733424fc9fSMichal Privoznik 
7743424fc9fSMichal Privoznik             memset(&ifr, 0, sizeof(ifr));
7753424fc9fSMichal Privoznik             strncpy(ifr.ifr_name,  info->value->name, IF_NAMESIZE);
7763424fc9fSMichal Privoznik             if (ioctl(sock, SIOCGIFHWADDR, &ifr) == -1) {
7773424fc9fSMichal Privoznik                 snprintf(err_msg, sizeof(err_msg),
7783424fc9fSMichal Privoznik                          "failed to get MAC addres of %s: %s",
7793424fc9fSMichal Privoznik                          ifa->ifa_name,
7803424fc9fSMichal Privoznik                          strerror(errno));
7813424fc9fSMichal Privoznik                 error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg);
7823424fc9fSMichal Privoznik                 goto error;
7833424fc9fSMichal Privoznik             }
7843424fc9fSMichal Privoznik 
7853424fc9fSMichal Privoznik             mac_addr = (unsigned char *) &ifr.ifr_hwaddr.sa_data;
7863424fc9fSMichal Privoznik 
7873424fc9fSMichal Privoznik             if (asprintf(&info->value->hardware_address,
7883424fc9fSMichal Privoznik                          "%02x:%02x:%02x:%02x:%02x:%02x",
7893424fc9fSMichal Privoznik                          (int) mac_addr[0], (int) mac_addr[1],
7903424fc9fSMichal Privoznik                          (int) mac_addr[2], (int) mac_addr[3],
7913424fc9fSMichal Privoznik                          (int) mac_addr[4], (int) mac_addr[5]) == -1) {
7923424fc9fSMichal Privoznik                 snprintf(err_msg, sizeof(err_msg),
7933424fc9fSMichal Privoznik                          "failed to format MAC: %s", strerror(errno));
7943424fc9fSMichal Privoznik                 error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg);
7953424fc9fSMichal Privoznik                 goto error;
7963424fc9fSMichal Privoznik             }
7973424fc9fSMichal Privoznik 
7983424fc9fSMichal Privoznik             info->value->has_hardware_address = true;
7993424fc9fSMichal Privoznik             close(sock);
8003424fc9fSMichal Privoznik         }
8013424fc9fSMichal Privoznik 
8023424fc9fSMichal Privoznik         if (ifa->ifa_addr &&
8033424fc9fSMichal Privoznik             ifa->ifa_addr->sa_family == AF_INET) {
8043424fc9fSMichal Privoznik             /* interface with IPv4 address */
8053424fc9fSMichal Privoznik             address_item = g_malloc0(sizeof(*address_item));
8063424fc9fSMichal Privoznik             address_item->value = g_malloc0(sizeof(*address_item->value));
8073424fc9fSMichal Privoznik             p = &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
8083424fc9fSMichal Privoznik             if (!inet_ntop(AF_INET, p, addr4, sizeof(addr4))) {
8093424fc9fSMichal Privoznik                 snprintf(err_msg, sizeof(err_msg),
8103424fc9fSMichal Privoznik                          "inet_ntop failed : %s", strerror(errno));
8113424fc9fSMichal Privoznik                 error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg);
8123424fc9fSMichal Privoznik                 goto error;
8133424fc9fSMichal Privoznik             }
8143424fc9fSMichal Privoznik 
8153424fc9fSMichal Privoznik             address_item->value->ip_address = g_strdup(addr4);
8163424fc9fSMichal Privoznik             address_item->value->ip_address_type = GUEST_IP_ADDRESS_TYPE_IPV4;
8173424fc9fSMichal Privoznik 
8183424fc9fSMichal Privoznik             if (ifa->ifa_netmask) {
8193424fc9fSMichal Privoznik                 /* Count the number of set bits in netmask.
8203424fc9fSMichal Privoznik                  * This is safe as '1' and '0' cannot be shuffled in netmask. */
8213424fc9fSMichal Privoznik                 p = &((struct sockaddr_in *)ifa->ifa_netmask)->sin_addr;
8223424fc9fSMichal Privoznik                 address_item->value->prefix = ctpop32(((uint32_t *) p)[0]);
8233424fc9fSMichal Privoznik             }
8243424fc9fSMichal Privoznik         } else if (ifa->ifa_addr &&
8253424fc9fSMichal Privoznik                    ifa->ifa_addr->sa_family == AF_INET6) {
8263424fc9fSMichal Privoznik             /* interface with IPv6 address */
8273424fc9fSMichal Privoznik             address_item = g_malloc0(sizeof(*address_item));
8283424fc9fSMichal Privoznik             address_item->value = g_malloc0(sizeof(*address_item->value));
8293424fc9fSMichal Privoznik             p = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
8303424fc9fSMichal Privoznik             if (!inet_ntop(AF_INET6, p, addr6, sizeof(addr6))) {
8313424fc9fSMichal Privoznik                 snprintf(err_msg, sizeof(err_msg),
8323424fc9fSMichal Privoznik                          "inet_ntop failed : %s", strerror(errno));
8333424fc9fSMichal Privoznik                 error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg);
8343424fc9fSMichal Privoznik                 goto error;
8353424fc9fSMichal Privoznik             }
8363424fc9fSMichal Privoznik 
8373424fc9fSMichal Privoznik             address_item->value->ip_address = g_strdup(addr6);
8383424fc9fSMichal Privoznik             address_item->value->ip_address_type = GUEST_IP_ADDRESS_TYPE_IPV6;
8393424fc9fSMichal Privoznik 
8403424fc9fSMichal Privoznik             if (ifa->ifa_netmask) {
8413424fc9fSMichal Privoznik                 /* Count the number of set bits in netmask.
8423424fc9fSMichal Privoznik                  * This is safe as '1' and '0' cannot be shuffled in netmask. */
8433424fc9fSMichal Privoznik                 p = &((struct sockaddr_in6 *)ifa->ifa_netmask)->sin6_addr;
8443424fc9fSMichal Privoznik                 address_item->value->prefix =
8453424fc9fSMichal Privoznik                     ctpop32(((uint32_t *) p)[0]) +
8463424fc9fSMichal Privoznik                     ctpop32(((uint32_t *) p)[1]) +
8473424fc9fSMichal Privoznik                     ctpop32(((uint32_t *) p)[2]) +
8483424fc9fSMichal Privoznik                     ctpop32(((uint32_t *) p)[3]);
8493424fc9fSMichal Privoznik             }
8503424fc9fSMichal Privoznik         }
8513424fc9fSMichal Privoznik 
8523424fc9fSMichal Privoznik         if (!address_item) {
8533424fc9fSMichal Privoznik             continue;
8543424fc9fSMichal Privoznik         }
8553424fc9fSMichal Privoznik 
8563424fc9fSMichal Privoznik         address_list = &info->value->ip_addresses;
8573424fc9fSMichal Privoznik 
8583424fc9fSMichal Privoznik         while (*address_list && (*address_list)->next) {
8593424fc9fSMichal Privoznik             address_list = &(*address_list)->next;
8603424fc9fSMichal Privoznik         }
8613424fc9fSMichal Privoznik 
8623424fc9fSMichal Privoznik         if (!*address_list) {
8633424fc9fSMichal Privoznik             *address_list = address_item;
8643424fc9fSMichal Privoznik         } else {
8653424fc9fSMichal Privoznik             (*address_list)->next = address_item;
8663424fc9fSMichal Privoznik         }
8673424fc9fSMichal Privoznik 
8683424fc9fSMichal Privoznik         info->value->has_ip_addresses = true;
8693424fc9fSMichal Privoznik 
8703424fc9fSMichal Privoznik 
8713424fc9fSMichal Privoznik     }
8723424fc9fSMichal Privoznik 
8733424fc9fSMichal Privoznik     freeifaddrs(ifap);
8743424fc9fSMichal Privoznik     return head;
8753424fc9fSMichal Privoznik 
8763424fc9fSMichal Privoznik error:
8773424fc9fSMichal Privoznik     freeifaddrs(ifap);
8783424fc9fSMichal Privoznik     qapi_free_GuestNetworkInterfaceList(head);
8793424fc9fSMichal Privoznik     return NULL;
8803424fc9fSMichal Privoznik }
8813424fc9fSMichal Privoznik 
882e72c3f2eSMichael Roth #else /* defined(__linux__) */
883e72c3f2eSMichael Roth 
884e72c3f2eSMichael Roth void qmp_guest_suspend_disk(Error **err)
885e72c3f2eSMichael Roth {
886e72c3f2eSMichael Roth     error_set(err, QERR_UNSUPPORTED);
887e72c3f2eSMichael Roth }
888e72c3f2eSMichael Roth 
889e72c3f2eSMichael Roth void qmp_guest_suspend_ram(Error **err)
890e72c3f2eSMichael Roth {
891e72c3f2eSMichael Roth     error_set(err, QERR_UNSUPPORTED);
892e72c3f2eSMichael Roth }
893e72c3f2eSMichael Roth 
894e72c3f2eSMichael Roth void qmp_guest_suspend_hybrid(Error **err)
895e72c3f2eSMichael Roth {
896e72c3f2eSMichael Roth     error_set(err, QERR_UNSUPPORTED);
897e72c3f2eSMichael Roth }
898e72c3f2eSMichael Roth 
899e72c3f2eSMichael Roth GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp)
900e72c3f2eSMichael Roth {
901e72c3f2eSMichael Roth     error_set(errp, QERR_UNSUPPORTED);
902e72c3f2eSMichael Roth     return NULL;
903e72c3f2eSMichael Roth }
904e72c3f2eSMichael Roth 
905e72c3f2eSMichael Roth #endif
906e72c3f2eSMichael Roth 
907*d35d4cb5SMichael Roth #if !defined(CONFIG_FSFREEZE)
908*d35d4cb5SMichael Roth 
909*d35d4cb5SMichael Roth GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **err)
910*d35d4cb5SMichael Roth {
911*d35d4cb5SMichael Roth     error_set(err, QERR_UNSUPPORTED);
912*d35d4cb5SMichael Roth 
913*d35d4cb5SMichael Roth     return 0;
914*d35d4cb5SMichael Roth }
915*d35d4cb5SMichael Roth 
916*d35d4cb5SMichael Roth int64_t qmp_guest_fsfreeze_freeze(Error **err)
917*d35d4cb5SMichael Roth {
918*d35d4cb5SMichael Roth     error_set(err, QERR_UNSUPPORTED);
919*d35d4cb5SMichael Roth 
920*d35d4cb5SMichael Roth     return 0;
921*d35d4cb5SMichael Roth }
922*d35d4cb5SMichael Roth 
923*d35d4cb5SMichael Roth int64_t qmp_guest_fsfreeze_thaw(Error **err)
924*d35d4cb5SMichael Roth {
925*d35d4cb5SMichael Roth     error_set(err, QERR_UNSUPPORTED);
926*d35d4cb5SMichael Roth 
927*d35d4cb5SMichael Roth     return 0;
928*d35d4cb5SMichael Roth }
929*d35d4cb5SMichael Roth 
930*d35d4cb5SMichael Roth #endif
931*d35d4cb5SMichael Roth 
932c216e5adSMichael Roth /* register init/cleanup routines for stateful command groups */
933c216e5adSMichael Roth void ga_command_state_init(GAState *s, GACommandState *cs)
934c216e5adSMichael Roth {
935c216e5adSMichael Roth #if defined(CONFIG_FSFREEZE)
936c216e5adSMichael Roth     ga_command_state_add(cs, guest_fsfreeze_init, guest_fsfreeze_cleanup);
937c216e5adSMichael Roth #endif
938c216e5adSMichael Roth     ga_command_state_add(cs, guest_file_init, NULL);
939c216e5adSMichael Roth }
940