xref: /openbmc/qemu/qga/commands-posix.c (revision d220a6df)
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"
211de7afc9SPaolo Bonzini #include "qemu/queue.h"
221de7afc9SPaolo 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 
49*d220a6dfSLuiz Capitulino static void ga_wait_child(pid_t pid, int *status, Error **err)
50*d220a6dfSLuiz Capitulino {
51*d220a6dfSLuiz Capitulino     pid_t rpid;
52*d220a6dfSLuiz Capitulino 
53*d220a6dfSLuiz Capitulino     *status = 0;
54*d220a6dfSLuiz Capitulino 
55*d220a6dfSLuiz Capitulino     do {
56*d220a6dfSLuiz Capitulino         rpid = waitpid(pid, status, 0);
57*d220a6dfSLuiz Capitulino     } while (rpid == -1 && errno == EINTR);
58*d220a6dfSLuiz Capitulino 
59*d220a6dfSLuiz Capitulino     if (rpid == -1) {
60*d220a6dfSLuiz Capitulino         error_setg_errno(err, errno, "failed to wait for child (pid: %d)", pid);
61*d220a6dfSLuiz Capitulino         return;
62*d220a6dfSLuiz Capitulino     }
63*d220a6dfSLuiz Capitulino 
64*d220a6dfSLuiz Capitulino     g_assert(rpid == pid);
65*d220a6dfSLuiz Capitulino }
66*d220a6dfSLuiz Capitulino 
67c216e5adSMichael Roth void qmp_guest_shutdown(bool has_mode, const char *mode, Error **err)
68c216e5adSMichael Roth {
69c216e5adSMichael Roth     const char *shutdown_flag;
70*d220a6dfSLuiz Capitulino     Error *local_err = NULL;
71*d220a6dfSLuiz Capitulino     pid_t pid;
723674838cSLuiz Capitulino     int status;
73c216e5adSMichael Roth 
74c216e5adSMichael Roth     slog("guest-shutdown called, mode: %s", mode);
75c216e5adSMichael Roth     if (!has_mode || strcmp(mode, "powerdown") == 0) {
76c216e5adSMichael Roth         shutdown_flag = "-P";
77c216e5adSMichael Roth     } else if (strcmp(mode, "halt") == 0) {
78c216e5adSMichael Roth         shutdown_flag = "-H";
79c216e5adSMichael Roth     } else if (strcmp(mode, "reboot") == 0) {
80c216e5adSMichael Roth         shutdown_flag = "-r";
81c216e5adSMichael Roth     } else {
82*d220a6dfSLuiz Capitulino         error_setg(err,
83*d220a6dfSLuiz Capitulino                    "mode is invalid (valid values are: halt|powerdown|reboot");
84c216e5adSMichael Roth         return;
85c216e5adSMichael Roth     }
86c216e5adSMichael Roth 
87d5dd3498SLuiz Capitulino     pid = fork();
88d5dd3498SLuiz Capitulino     if (pid == 0) {
89c216e5adSMichael Roth         /* child, start the shutdown */
90c216e5adSMichael Roth         setsid();
913674838cSLuiz Capitulino         reopen_fd_to_null(0);
923674838cSLuiz Capitulino         reopen_fd_to_null(1);
933674838cSLuiz Capitulino         reopen_fd_to_null(2);
94c216e5adSMichael Roth 
953674838cSLuiz Capitulino         execle("/sbin/shutdown", "shutdown", shutdown_flag, "+0",
963674838cSLuiz Capitulino                "hypervisor initiated shutdown", (char*)NULL, environ);
973674838cSLuiz Capitulino         _exit(EXIT_FAILURE);
98d5dd3498SLuiz Capitulino     } else if (pid < 0) {
99*d220a6dfSLuiz Capitulino         error_setg_errno(err, errno, "failed to create child process");
100d5dd3498SLuiz Capitulino         return;
101d5dd3498SLuiz Capitulino     }
102d5dd3498SLuiz Capitulino 
103*d220a6dfSLuiz Capitulino     ga_wait_child(pid, &status, &local_err);
104*d220a6dfSLuiz Capitulino     if (error_is_set(&local_err)) {
105*d220a6dfSLuiz Capitulino         error_propagate(err, local_err);
106*d220a6dfSLuiz Capitulino         return;
107*d220a6dfSLuiz Capitulino     }
108*d220a6dfSLuiz Capitulino 
109*d220a6dfSLuiz Capitulino     if (!WIFEXITED(status)) {
110*d220a6dfSLuiz Capitulino         error_setg(err, "child process has terminated abnormally");
111*d220a6dfSLuiz Capitulino         return;
112*d220a6dfSLuiz Capitulino     }
113*d220a6dfSLuiz Capitulino 
114*d220a6dfSLuiz Capitulino     if (WEXITSTATUS(status)) {
115*d220a6dfSLuiz Capitulino         error_setg(err, "child process has failed to shutdown");
116*d220a6dfSLuiz Capitulino         return;
117*d220a6dfSLuiz Capitulino     }
118*d220a6dfSLuiz Capitulino 
119*d220a6dfSLuiz Capitulino     /* succeded */
120c216e5adSMichael Roth }
121c216e5adSMichael Roth 
122c216e5adSMichael Roth typedef struct GuestFileHandle {
123c216e5adSMichael Roth     uint64_t id;
124c216e5adSMichael Roth     FILE *fh;
125c216e5adSMichael Roth     QTAILQ_ENTRY(GuestFileHandle) next;
126c216e5adSMichael Roth } GuestFileHandle;
127c216e5adSMichael Roth 
128c216e5adSMichael Roth static struct {
129c216e5adSMichael Roth     QTAILQ_HEAD(, GuestFileHandle) filehandles;
130c216e5adSMichael Roth } guest_file_state;
131c216e5adSMichael Roth 
132c216e5adSMichael Roth static void guest_file_handle_add(FILE *fh)
133c216e5adSMichael Roth {
134c216e5adSMichael Roth     GuestFileHandle *gfh;
135c216e5adSMichael Roth 
136c216e5adSMichael Roth     gfh = g_malloc0(sizeof(GuestFileHandle));
137c216e5adSMichael Roth     gfh->id = fileno(fh);
138c216e5adSMichael Roth     gfh->fh = fh;
139c216e5adSMichael Roth     QTAILQ_INSERT_TAIL(&guest_file_state.filehandles, gfh, next);
140c216e5adSMichael Roth }
141c216e5adSMichael Roth 
142a9de6d01SLuiz Capitulino static GuestFileHandle *guest_file_handle_find(int64_t id, Error **err)
143c216e5adSMichael Roth {
144c216e5adSMichael Roth     GuestFileHandle *gfh;
145c216e5adSMichael Roth 
146c216e5adSMichael Roth     QTAILQ_FOREACH(gfh, &guest_file_state.filehandles, next)
147c216e5adSMichael Roth     {
148c216e5adSMichael Roth         if (gfh->id == id) {
149c216e5adSMichael Roth             return gfh;
150c216e5adSMichael Roth         }
151c216e5adSMichael Roth     }
152c216e5adSMichael Roth 
153a9de6d01SLuiz Capitulino     error_setg(err, "handle '%" PRId64 "' has not been found", id);
154c216e5adSMichael Roth     return NULL;
155c216e5adSMichael Roth }
156c216e5adSMichael Roth 
157c216e5adSMichael Roth int64_t qmp_guest_file_open(const char *path, bool has_mode, const char *mode, Error **err)
158c216e5adSMichael Roth {
159c216e5adSMichael Roth     FILE *fh;
160c216e5adSMichael Roth     int fd;
161c216e5adSMichael Roth     int64_t ret = -1;
162c216e5adSMichael Roth 
163c216e5adSMichael Roth     if (!has_mode) {
164c216e5adSMichael Roth         mode = "r";
165c216e5adSMichael Roth     }
166c216e5adSMichael Roth     slog("guest-file-open called, filepath: %s, mode: %s", path, mode);
167c216e5adSMichael Roth     fh = fopen(path, mode);
168c216e5adSMichael Roth     if (!fh) {
169db3edb66SLuiz Capitulino         error_setg_errno(err, errno, "failed to open file '%s' (mode: '%s')",
170db3edb66SLuiz Capitulino                          path, mode);
171c216e5adSMichael Roth         return -1;
172c216e5adSMichael Roth     }
173c216e5adSMichael Roth 
174c216e5adSMichael Roth     /* set fd non-blocking to avoid common use cases (like reading from a
175c216e5adSMichael Roth      * named pipe) from hanging the agent
176c216e5adSMichael Roth      */
177c216e5adSMichael Roth     fd = fileno(fh);
178c216e5adSMichael Roth     ret = fcntl(fd, F_GETFL);
179c216e5adSMichael Roth     ret = fcntl(fd, F_SETFL, ret | O_NONBLOCK);
180c216e5adSMichael Roth     if (ret == -1) {
181db3edb66SLuiz Capitulino         error_setg_errno(err, errno, "failed to make file '%s' non-blocking",
182db3edb66SLuiz Capitulino                          path);
183c216e5adSMichael Roth         fclose(fh);
184c216e5adSMichael Roth         return -1;
185c216e5adSMichael Roth     }
186c216e5adSMichael Roth 
187c216e5adSMichael Roth     guest_file_handle_add(fh);
188c216e5adSMichael Roth     slog("guest-file-open, handle: %d", fd);
189c216e5adSMichael Roth     return fd;
190c216e5adSMichael Roth }
191c216e5adSMichael Roth 
192c216e5adSMichael Roth void qmp_guest_file_close(int64_t handle, Error **err)
193c216e5adSMichael Roth {
194a9de6d01SLuiz Capitulino     GuestFileHandle *gfh = guest_file_handle_find(handle, err);
195c216e5adSMichael Roth     int ret;
196c216e5adSMichael Roth 
197c216e5adSMichael Roth     slog("guest-file-close called, handle: %ld", handle);
198c216e5adSMichael Roth     if (!gfh) {
199c216e5adSMichael Roth         return;
200c216e5adSMichael Roth     }
201c216e5adSMichael Roth 
202c216e5adSMichael Roth     ret = fclose(gfh->fh);
2033ac4b7c5SLuiz Capitulino     if (ret == EOF) {
204db3edb66SLuiz Capitulino         error_setg_errno(err, errno, "failed to close handle");
205c216e5adSMichael Roth         return;
206c216e5adSMichael Roth     }
207c216e5adSMichael Roth 
208c216e5adSMichael Roth     QTAILQ_REMOVE(&guest_file_state.filehandles, gfh, next);
209c216e5adSMichael Roth     g_free(gfh);
210c216e5adSMichael Roth }
211c216e5adSMichael Roth 
212c216e5adSMichael Roth struct GuestFileRead *qmp_guest_file_read(int64_t handle, bool has_count,
213c216e5adSMichael Roth                                           int64_t count, Error **err)
214c216e5adSMichael Roth {
215a9de6d01SLuiz Capitulino     GuestFileHandle *gfh = guest_file_handle_find(handle, err);
216c216e5adSMichael Roth     GuestFileRead *read_data = NULL;
217c216e5adSMichael Roth     guchar *buf;
218c216e5adSMichael Roth     FILE *fh;
219c216e5adSMichael Roth     size_t read_count;
220c216e5adSMichael Roth 
221c216e5adSMichael Roth     if (!gfh) {
222c216e5adSMichael Roth         return NULL;
223c216e5adSMichael Roth     }
224c216e5adSMichael Roth 
225c216e5adSMichael Roth     if (!has_count) {
226c216e5adSMichael Roth         count = QGA_READ_COUNT_DEFAULT;
227c216e5adSMichael Roth     } else if (count < 0) {
228db3edb66SLuiz Capitulino         error_setg(err, "value '%" PRId64 "' is invalid for argument count",
229db3edb66SLuiz Capitulino                    count);
230c216e5adSMichael Roth         return NULL;
231c216e5adSMichael Roth     }
232c216e5adSMichael Roth 
233c216e5adSMichael Roth     fh = gfh->fh;
234c216e5adSMichael Roth     buf = g_malloc0(count+1);
235c216e5adSMichael Roth     read_count = fread(buf, 1, count, fh);
236c216e5adSMichael Roth     if (ferror(fh)) {
237db3edb66SLuiz Capitulino         error_setg_errno(err, errno, "failed to read file");
238c216e5adSMichael Roth         slog("guest-file-read failed, handle: %ld", handle);
239c216e5adSMichael Roth     } else {
240c216e5adSMichael Roth         buf[read_count] = 0;
241c216e5adSMichael Roth         read_data = g_malloc0(sizeof(GuestFileRead));
242c216e5adSMichael Roth         read_data->count = read_count;
243c216e5adSMichael Roth         read_data->eof = feof(fh);
244c216e5adSMichael Roth         if (read_count) {
245c216e5adSMichael Roth             read_data->buf_b64 = g_base64_encode(buf, read_count);
246c216e5adSMichael Roth         }
247c216e5adSMichael Roth     }
248c216e5adSMichael Roth     g_free(buf);
249c216e5adSMichael Roth     clearerr(fh);
250c216e5adSMichael Roth 
251c216e5adSMichael Roth     return read_data;
252c216e5adSMichael Roth }
253c216e5adSMichael Roth 
254c216e5adSMichael Roth GuestFileWrite *qmp_guest_file_write(int64_t handle, const char *buf_b64,
255c216e5adSMichael Roth                                      bool has_count, int64_t count, Error **err)
256c216e5adSMichael Roth {
257c216e5adSMichael Roth     GuestFileWrite *write_data = NULL;
258c216e5adSMichael Roth     guchar *buf;
259c216e5adSMichael Roth     gsize buf_len;
260c216e5adSMichael Roth     int write_count;
261a9de6d01SLuiz Capitulino     GuestFileHandle *gfh = guest_file_handle_find(handle, err);
262c216e5adSMichael Roth     FILE *fh;
263c216e5adSMichael Roth 
264c216e5adSMichael Roth     if (!gfh) {
265c216e5adSMichael Roth         return NULL;
266c216e5adSMichael Roth     }
267c216e5adSMichael Roth 
268c216e5adSMichael Roth     fh = gfh->fh;
269c216e5adSMichael Roth     buf = g_base64_decode(buf_b64, &buf_len);
270c216e5adSMichael Roth 
271c216e5adSMichael Roth     if (!has_count) {
272c216e5adSMichael Roth         count = buf_len;
273c216e5adSMichael Roth     } else if (count < 0 || count > buf_len) {
274db3edb66SLuiz Capitulino         error_setg(err, "value '%" PRId64 "' is invalid for argument count",
275db3edb66SLuiz Capitulino                    count);
276c216e5adSMichael Roth         g_free(buf);
277c216e5adSMichael Roth         return NULL;
278c216e5adSMichael Roth     }
279c216e5adSMichael Roth 
280c216e5adSMichael Roth     write_count = fwrite(buf, 1, count, fh);
281c216e5adSMichael Roth     if (ferror(fh)) {
282db3edb66SLuiz Capitulino         error_setg_errno(err, errno, "failed to write to file");
283c216e5adSMichael Roth         slog("guest-file-write failed, handle: %ld", handle);
284c216e5adSMichael Roth     } else {
285c216e5adSMichael Roth         write_data = g_malloc0(sizeof(GuestFileWrite));
286c216e5adSMichael Roth         write_data->count = write_count;
287c216e5adSMichael Roth         write_data->eof = feof(fh);
288c216e5adSMichael Roth     }
289c216e5adSMichael Roth     g_free(buf);
290c216e5adSMichael Roth     clearerr(fh);
291c216e5adSMichael Roth 
292c216e5adSMichael Roth     return write_data;
293c216e5adSMichael Roth }
294c216e5adSMichael Roth 
295c216e5adSMichael Roth struct GuestFileSeek *qmp_guest_file_seek(int64_t handle, int64_t offset,
296c216e5adSMichael Roth                                           int64_t whence, Error **err)
297c216e5adSMichael Roth {
298a9de6d01SLuiz Capitulino     GuestFileHandle *gfh = guest_file_handle_find(handle, err);
299c216e5adSMichael Roth     GuestFileSeek *seek_data = NULL;
300c216e5adSMichael Roth     FILE *fh;
301c216e5adSMichael Roth     int ret;
302c216e5adSMichael Roth 
303c216e5adSMichael Roth     if (!gfh) {
304c216e5adSMichael Roth         return NULL;
305c216e5adSMichael Roth     }
306c216e5adSMichael Roth 
307c216e5adSMichael Roth     fh = gfh->fh;
308c216e5adSMichael Roth     ret = fseek(fh, offset, whence);
309c216e5adSMichael Roth     if (ret == -1) {
310db3edb66SLuiz Capitulino         error_setg_errno(err, errno, "failed to seek file");
311c216e5adSMichael Roth     } else {
312c216e5adSMichael Roth         seek_data = g_malloc0(sizeof(GuestFileRead));
313c216e5adSMichael Roth         seek_data->position = ftell(fh);
314c216e5adSMichael Roth         seek_data->eof = feof(fh);
315c216e5adSMichael Roth     }
316c216e5adSMichael Roth     clearerr(fh);
317c216e5adSMichael Roth 
318c216e5adSMichael Roth     return seek_data;
319c216e5adSMichael Roth }
320c216e5adSMichael Roth 
321c216e5adSMichael Roth void qmp_guest_file_flush(int64_t handle, Error **err)
322c216e5adSMichael Roth {
323a9de6d01SLuiz Capitulino     GuestFileHandle *gfh = guest_file_handle_find(handle, err);
324c216e5adSMichael Roth     FILE *fh;
325c216e5adSMichael Roth     int ret;
326c216e5adSMichael Roth 
327c216e5adSMichael Roth     if (!gfh) {
328c216e5adSMichael Roth         return;
329c216e5adSMichael Roth     }
330c216e5adSMichael Roth 
331c216e5adSMichael Roth     fh = gfh->fh;
332c216e5adSMichael Roth     ret = fflush(fh);
333c216e5adSMichael Roth     if (ret == EOF) {
334db3edb66SLuiz Capitulino         error_setg_errno(err, errno, "failed to flush file");
335c216e5adSMichael Roth     }
336c216e5adSMichael Roth }
337c216e5adSMichael Roth 
338c216e5adSMichael Roth static void guest_file_init(void)
339c216e5adSMichael Roth {
340c216e5adSMichael Roth     QTAILQ_INIT(&guest_file_state.filehandles);
341c216e5adSMichael Roth }
342c216e5adSMichael Roth 
343e72c3f2eSMichael Roth /* linux-specific implementations. avoid this if at all possible. */
344e72c3f2eSMichael Roth #if defined(__linux__)
345e72c3f2eSMichael Roth 
346eab5fd59SPaolo Bonzini #if defined(CONFIG_FSFREEZE) || defined(CONFIG_FSTRIM)
347af02203fSPaolo Bonzini typedef struct FsMount {
348c216e5adSMichael Roth     char *dirname;
349c216e5adSMichael Roth     char *devtype;
350af02203fSPaolo Bonzini     QTAILQ_ENTRY(FsMount) next;
351af02203fSPaolo Bonzini } FsMount;
352c216e5adSMichael Roth 
353af02203fSPaolo Bonzini typedef QTAILQ_HEAD(, FsMount) FsMountList;
3549e8aded4SMichael Roth 
355af02203fSPaolo Bonzini static void free_fs_mount_list(FsMountList *mounts)
356c216e5adSMichael Roth {
357af02203fSPaolo Bonzini      FsMount *mount, *temp;
358c216e5adSMichael Roth 
3599e8aded4SMichael Roth      if (!mounts) {
3609e8aded4SMichael Roth          return;
3619e8aded4SMichael Roth      }
3629e8aded4SMichael Roth 
3639e8aded4SMichael Roth      QTAILQ_FOREACH_SAFE(mount, mounts, next, temp) {
3649e8aded4SMichael Roth          QTAILQ_REMOVE(mounts, mount, next);
365c216e5adSMichael Roth          g_free(mount->dirname);
366c216e5adSMichael Roth          g_free(mount->devtype);
367c216e5adSMichael Roth          g_free(mount);
368c216e5adSMichael Roth      }
3699e8aded4SMichael Roth }
3709e8aded4SMichael Roth 
3719e8aded4SMichael Roth /*
3729e8aded4SMichael Roth  * Walk the mount table and build a list of local file systems
3739e8aded4SMichael Roth  */
374af02203fSPaolo Bonzini static int build_fs_mount_list(FsMountList *mounts)
3759e8aded4SMichael Roth {
3769e8aded4SMichael Roth     struct mntent *ment;
377af02203fSPaolo Bonzini     FsMount *mount;
3789e2fa418SMichael Roth     char const *mtab = "/proc/self/mounts";
3799e8aded4SMichael Roth     FILE *fp;
380c216e5adSMichael Roth 
381c216e5adSMichael Roth     fp = setmntent(mtab, "r");
382c216e5adSMichael Roth     if (!fp) {
383c216e5adSMichael Roth         g_warning("fsfreeze: unable to read mtab");
384c216e5adSMichael Roth         return -1;
385c216e5adSMichael Roth     }
386c216e5adSMichael Roth 
387c216e5adSMichael Roth     while ((ment = getmntent(fp))) {
388c216e5adSMichael Roth         /*
389c216e5adSMichael Roth          * An entry which device name doesn't start with a '/' is
390c216e5adSMichael Roth          * either a dummy file system or a network file system.
391c216e5adSMichael Roth          * Add special handling for smbfs and cifs as is done by
392c216e5adSMichael Roth          * coreutils as well.
393c216e5adSMichael Roth          */
394c216e5adSMichael Roth         if ((ment->mnt_fsname[0] != '/') ||
395c216e5adSMichael Roth             (strcmp(ment->mnt_type, "smbfs") == 0) ||
396c216e5adSMichael Roth             (strcmp(ment->mnt_type, "cifs") == 0)) {
397c216e5adSMichael Roth             continue;
398c216e5adSMichael Roth         }
399c216e5adSMichael Roth 
400af02203fSPaolo Bonzini         mount = g_malloc0(sizeof(FsMount));
401c216e5adSMichael Roth         mount->dirname = g_strdup(ment->mnt_dir);
402c216e5adSMichael Roth         mount->devtype = g_strdup(ment->mnt_type);
403c216e5adSMichael Roth 
4049e8aded4SMichael Roth         QTAILQ_INSERT_TAIL(mounts, mount, next);
405c216e5adSMichael Roth     }
406c216e5adSMichael Roth 
407c216e5adSMichael Roth     endmntent(fp);
408c216e5adSMichael Roth 
409c216e5adSMichael Roth     return 0;
410c216e5adSMichael Roth }
411eab5fd59SPaolo Bonzini #endif
412eab5fd59SPaolo Bonzini 
413eab5fd59SPaolo Bonzini #if defined(CONFIG_FSFREEZE)
414c216e5adSMichael Roth 
415c216e5adSMichael Roth /*
416c216e5adSMichael Roth  * Return status of freeze/thaw
417c216e5adSMichael Roth  */
418c216e5adSMichael Roth GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **err)
419c216e5adSMichael Roth {
420f22d85e9SMichael Roth     if (ga_is_frozen(ga_state)) {
421f22d85e9SMichael Roth         return GUEST_FSFREEZE_STATUS_FROZEN;
422f22d85e9SMichael Roth     }
423f22d85e9SMichael Roth 
424f22d85e9SMichael Roth     return GUEST_FSFREEZE_STATUS_THAWED;
425c216e5adSMichael Roth }
426c216e5adSMichael Roth 
427c216e5adSMichael Roth /*
428c216e5adSMichael Roth  * Walk list of mounted file systems in the guest, and freeze the ones which
429c216e5adSMichael Roth  * are real local file systems.
430c216e5adSMichael Roth  */
431c216e5adSMichael Roth int64_t qmp_guest_fsfreeze_freeze(Error **err)
432c216e5adSMichael Roth {
433c216e5adSMichael Roth     int ret = 0, i = 0;
434af02203fSPaolo Bonzini     FsMountList mounts;
435af02203fSPaolo Bonzini     struct FsMount *mount;
436c216e5adSMichael Roth     int fd;
437c216e5adSMichael Roth     char err_msg[512];
438c216e5adSMichael Roth 
439c216e5adSMichael Roth     slog("guest-fsfreeze called");
440c216e5adSMichael Roth 
4419e8aded4SMichael Roth     QTAILQ_INIT(&mounts);
442af02203fSPaolo Bonzini     ret = build_fs_mount_list(&mounts);
443c216e5adSMichael Roth     if (ret < 0) {
444c216e5adSMichael Roth         return ret;
445c216e5adSMichael Roth     }
446c216e5adSMichael Roth 
447c216e5adSMichael Roth     /* cannot risk guest agent blocking itself on a write in this state */
448f22d85e9SMichael Roth     ga_set_frozen(ga_state);
449c216e5adSMichael Roth 
4509e8aded4SMichael Roth     QTAILQ_FOREACH(mount, &mounts, next) {
451c216e5adSMichael Roth         fd = qemu_open(mount->dirname, O_RDONLY);
452c216e5adSMichael Roth         if (fd == -1) {
4539e8aded4SMichael Roth             sprintf(err_msg, "failed to open %s, %s", mount->dirname,
4549e8aded4SMichael Roth                     strerror(errno));
455c216e5adSMichael Roth             error_set(err, QERR_QGA_COMMAND_FAILED, err_msg);
456c216e5adSMichael Roth             goto error;
457c216e5adSMichael Roth         }
458c216e5adSMichael Roth 
459c216e5adSMichael Roth         /* we try to cull filesytems we know won't work in advance, but other
460c216e5adSMichael Roth          * filesytems may not implement fsfreeze for less obvious reasons.
4619e8aded4SMichael Roth          * these will report EOPNOTSUPP. we simply ignore these when tallying
4629e8aded4SMichael Roth          * the number of frozen filesystems.
4639e8aded4SMichael Roth          *
4649e8aded4SMichael Roth          * any other error means a failure to freeze a filesystem we
4659e8aded4SMichael Roth          * expect to be freezable, so return an error in those cases
4669e8aded4SMichael Roth          * and return system to thawed state.
467c216e5adSMichael Roth          */
468c216e5adSMichael Roth         ret = ioctl(fd, FIFREEZE);
4699e8aded4SMichael Roth         if (ret == -1) {
4709e8aded4SMichael Roth             if (errno != EOPNOTSUPP) {
4719e8aded4SMichael Roth                 sprintf(err_msg, "failed to freeze %s, %s",
4729e8aded4SMichael Roth                         mount->dirname, strerror(errno));
473c216e5adSMichael Roth                 error_set(err, QERR_QGA_COMMAND_FAILED, err_msg);
474c216e5adSMichael Roth                 close(fd);
475c216e5adSMichael Roth                 goto error;
476c216e5adSMichael Roth             }
4779e8aded4SMichael Roth         } else {
478c216e5adSMichael Roth             i++;
479c216e5adSMichael Roth         }
4809e8aded4SMichael Roth         close(fd);
4819e8aded4SMichael Roth     }
482c216e5adSMichael Roth 
483af02203fSPaolo Bonzini     free_fs_mount_list(&mounts);
484c216e5adSMichael Roth     return i;
485c216e5adSMichael Roth 
486c216e5adSMichael Roth error:
487af02203fSPaolo Bonzini     free_fs_mount_list(&mounts);
488c216e5adSMichael Roth     qmp_guest_fsfreeze_thaw(NULL);
489c216e5adSMichael Roth     return 0;
490c216e5adSMichael Roth }
491c216e5adSMichael Roth 
492c216e5adSMichael Roth /*
493c216e5adSMichael Roth  * Walk list of frozen file systems in the guest, and thaw them.
494c216e5adSMichael Roth  */
495c216e5adSMichael Roth int64_t qmp_guest_fsfreeze_thaw(Error **err)
496c216e5adSMichael Roth {
497c216e5adSMichael Roth     int ret;
498af02203fSPaolo Bonzini     FsMountList mounts;
499af02203fSPaolo Bonzini     FsMount *mount;
5009e8aded4SMichael Roth     int fd, i = 0, logged;
501c216e5adSMichael Roth 
5029e8aded4SMichael Roth     QTAILQ_INIT(&mounts);
503af02203fSPaolo Bonzini     ret = build_fs_mount_list(&mounts);
5049e8aded4SMichael Roth     if (ret) {
5059e8aded4SMichael Roth         error_set(err, QERR_QGA_COMMAND_FAILED,
5069e8aded4SMichael Roth                   "failed to enumerate filesystems");
5079e8aded4SMichael Roth         return 0;
5089e8aded4SMichael Roth     }
5099e8aded4SMichael Roth 
5109e8aded4SMichael Roth     QTAILQ_FOREACH(mount, &mounts, next) {
5119e8aded4SMichael Roth         logged = false;
512c216e5adSMichael Roth         fd = qemu_open(mount->dirname, O_RDONLY);
513c216e5adSMichael Roth         if (fd == -1) {
514c216e5adSMichael Roth             continue;
515c216e5adSMichael Roth         }
5169e8aded4SMichael Roth         /* we have no way of knowing whether a filesystem was actually unfrozen
5179e8aded4SMichael Roth          * as a result of a successful call to FITHAW, only that if an error
5189e8aded4SMichael Roth          * was returned the filesystem was *not* unfrozen by that particular
5199e8aded4SMichael Roth          * call.
5209e8aded4SMichael Roth          *
521a31f0531SJim Meyering          * since multiple preceding FIFREEZEs require multiple calls to FITHAW
5229e8aded4SMichael Roth          * to unfreeze, continuing issuing FITHAW until an error is returned,
5239e8aded4SMichael Roth          * in which case either the filesystem is in an unfreezable state, or,
5249e8aded4SMichael Roth          * more likely, it was thawed previously (and remains so afterward).
5259e8aded4SMichael Roth          *
5269e8aded4SMichael Roth          * also, since the most recent successful call is the one that did
5279e8aded4SMichael Roth          * the actual unfreeze, we can use this to provide an accurate count
5289e8aded4SMichael Roth          * of the number of filesystems unfrozen by guest-fsfreeze-thaw, which
5299e8aded4SMichael Roth          * may * be useful for determining whether a filesystem was unfrozen
5309e8aded4SMichael Roth          * during the freeze/thaw phase by a process other than qemu-ga.
5319e8aded4SMichael Roth          */
5329e8aded4SMichael Roth         do {
533c216e5adSMichael Roth             ret = ioctl(fd, FITHAW);
5349e8aded4SMichael Roth             if (ret == 0 && !logged) {
535c216e5adSMichael Roth                 i++;
5369e8aded4SMichael Roth                 logged = true;
5379e8aded4SMichael Roth             }
5389e8aded4SMichael Roth         } while (ret == 0);
5399e8aded4SMichael Roth         close(fd);
540c216e5adSMichael Roth     }
541c216e5adSMichael Roth 
542f22d85e9SMichael Roth     ga_unset_frozen(ga_state);
543af02203fSPaolo Bonzini     free_fs_mount_list(&mounts);
544c216e5adSMichael Roth     return i;
545c216e5adSMichael Roth }
546c216e5adSMichael Roth 
547c216e5adSMichael Roth static void guest_fsfreeze_cleanup(void)
548c216e5adSMichael Roth {
549c216e5adSMichael Roth     int64_t ret;
550c216e5adSMichael Roth     Error *err = NULL;
551c216e5adSMichael Roth 
552f22d85e9SMichael Roth     if (ga_is_frozen(ga_state) == GUEST_FSFREEZE_STATUS_FROZEN) {
553c216e5adSMichael Roth         ret = qmp_guest_fsfreeze_thaw(&err);
554c216e5adSMichael Roth         if (ret < 0 || err) {
555c216e5adSMichael Roth             slog("failed to clean up frozen filesystems");
556c216e5adSMichael Roth         }
557c216e5adSMichael Roth     }
558c216e5adSMichael Roth }
559e72c3f2eSMichael Roth #endif /* CONFIG_FSFREEZE */
560c216e5adSMichael Roth 
561eab5fd59SPaolo Bonzini #if defined(CONFIG_FSTRIM)
562eab5fd59SPaolo Bonzini /*
563eab5fd59SPaolo Bonzini  * Walk list of mounted file systems in the guest, and trim them.
564eab5fd59SPaolo Bonzini  */
565eab5fd59SPaolo Bonzini void qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **err)
566eab5fd59SPaolo Bonzini {
567eab5fd59SPaolo Bonzini     int ret = 0;
568eab5fd59SPaolo Bonzini     FsMountList mounts;
569eab5fd59SPaolo Bonzini     struct FsMount *mount;
570eab5fd59SPaolo Bonzini     int fd;
571eab5fd59SPaolo Bonzini     char err_msg[512];
572eab5fd59SPaolo Bonzini     struct fstrim_range r = {
573eab5fd59SPaolo Bonzini         .start = 0,
574eab5fd59SPaolo Bonzini         .len = -1,
575eab5fd59SPaolo Bonzini         .minlen = has_minimum ? minimum : 0,
576eab5fd59SPaolo Bonzini     };
577eab5fd59SPaolo Bonzini 
578eab5fd59SPaolo Bonzini     slog("guest-fstrim called");
579eab5fd59SPaolo Bonzini 
580eab5fd59SPaolo Bonzini     QTAILQ_INIT(&mounts);
581eab5fd59SPaolo Bonzini     ret = build_fs_mount_list(&mounts);
582eab5fd59SPaolo Bonzini     if (ret < 0) {
583eab5fd59SPaolo Bonzini         return;
584eab5fd59SPaolo Bonzini     }
585eab5fd59SPaolo Bonzini 
586eab5fd59SPaolo Bonzini     QTAILQ_FOREACH(mount, &mounts, next) {
587eab5fd59SPaolo Bonzini         fd = qemu_open(mount->dirname, O_RDONLY);
588eab5fd59SPaolo Bonzini         if (fd == -1) {
589eab5fd59SPaolo Bonzini             sprintf(err_msg, "failed to open %s, %s", mount->dirname,
590eab5fd59SPaolo Bonzini                     strerror(errno));
591eab5fd59SPaolo Bonzini             error_set(err, QERR_QGA_COMMAND_FAILED, err_msg);
592eab5fd59SPaolo Bonzini             goto error;
593eab5fd59SPaolo Bonzini         }
594eab5fd59SPaolo Bonzini 
595eab5fd59SPaolo Bonzini         /* We try to cull filesytems we know won't work in advance, but other
596eab5fd59SPaolo Bonzini          * filesytems may not implement fstrim for less obvious reasons.  These
597eab5fd59SPaolo Bonzini          * will report EOPNOTSUPP; we simply ignore these errors.  Any other
598eab5fd59SPaolo Bonzini          * error means an unexpected error, so return it in those cases.  In
599eab5fd59SPaolo Bonzini          * some other cases ENOTTY will be reported (e.g. CD-ROMs).
600eab5fd59SPaolo Bonzini          */
601eab5fd59SPaolo Bonzini         ret = ioctl(fd, FITRIM, &r);
602eab5fd59SPaolo Bonzini         if (ret == -1) {
603eab5fd59SPaolo Bonzini             if (errno != ENOTTY && errno != EOPNOTSUPP) {
604eab5fd59SPaolo Bonzini                 sprintf(err_msg, "failed to trim %s, %s",
605eab5fd59SPaolo Bonzini                         mount->dirname, strerror(errno));
606eab5fd59SPaolo Bonzini                 error_set(err, QERR_QGA_COMMAND_FAILED, err_msg);
607eab5fd59SPaolo Bonzini                 close(fd);
608eab5fd59SPaolo Bonzini                 goto error;
609eab5fd59SPaolo Bonzini             }
610eab5fd59SPaolo Bonzini         }
611eab5fd59SPaolo Bonzini         close(fd);
612eab5fd59SPaolo Bonzini     }
613eab5fd59SPaolo Bonzini 
614eab5fd59SPaolo Bonzini error:
615eab5fd59SPaolo Bonzini     free_fs_mount_list(&mounts);
616eab5fd59SPaolo Bonzini }
617eab5fd59SPaolo Bonzini #endif /* CONFIG_FSTRIM */
618eab5fd59SPaolo Bonzini 
619eab5fd59SPaolo Bonzini 
62011d0f125SLuiz Capitulino #define LINUX_SYS_STATE_FILE "/sys/power/state"
62111d0f125SLuiz Capitulino #define SUSPEND_SUPPORTED 0
62211d0f125SLuiz Capitulino #define SUSPEND_NOT_SUPPORTED 1
62311d0f125SLuiz Capitulino 
62411d0f125SLuiz Capitulino static void bios_supports_mode(const char *pmutils_bin, const char *pmutils_arg,
62511d0f125SLuiz Capitulino                                const char *sysfile_str, Error **err)
62611d0f125SLuiz Capitulino {
62711d0f125SLuiz Capitulino     char *pmutils_path;
628dc8764f0SLuiz Capitulino     pid_t pid, rpid;
629dc8764f0SLuiz Capitulino     int status;
63011d0f125SLuiz Capitulino 
63111d0f125SLuiz Capitulino     pmutils_path = g_find_program_in_path(pmutils_bin);
63211d0f125SLuiz Capitulino 
63311d0f125SLuiz Capitulino     pid = fork();
63411d0f125SLuiz Capitulino     if (!pid) {
635dc8764f0SLuiz Capitulino         char buf[32]; /* hopefully big enough */
636dc8764f0SLuiz Capitulino         ssize_t ret;
637dc8764f0SLuiz 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, pmutils_arg, 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(SUSPEND_NOT_SUPPORTED);
65511d0f125SLuiz Capitulino         }
65611d0f125SLuiz Capitulino 
65711d0f125SLuiz Capitulino         fd = open(LINUX_SYS_STATE_FILE, O_RDONLY);
65811d0f125SLuiz Capitulino         if (fd < 0) {
65911d0f125SLuiz Capitulino             _exit(SUSPEND_NOT_SUPPORTED);
66011d0f125SLuiz Capitulino         }
66111d0f125SLuiz Capitulino 
66211d0f125SLuiz Capitulino         ret = read(fd, buf, sizeof(buf)-1);
66311d0f125SLuiz Capitulino         if (ret <= 0) {
66411d0f125SLuiz Capitulino             _exit(SUSPEND_NOT_SUPPORTED);
66511d0f125SLuiz Capitulino         }
66611d0f125SLuiz Capitulino         buf[ret] = '\0';
66711d0f125SLuiz Capitulino 
66811d0f125SLuiz Capitulino         if (strstr(buf, sysfile_str)) {
66911d0f125SLuiz Capitulino             _exit(SUSPEND_SUPPORTED);
67011d0f125SLuiz Capitulino         }
67111d0f125SLuiz Capitulino 
67211d0f125SLuiz Capitulino         _exit(SUSPEND_NOT_SUPPORTED);
67311d0f125SLuiz Capitulino     }
67411d0f125SLuiz Capitulino 
67511d0f125SLuiz Capitulino     g_free(pmutils_path);
67611d0f125SLuiz Capitulino 
67711d0f125SLuiz Capitulino     if (pid < 0) {
678dc8764f0SLuiz Capitulino         goto undef_err;
67911d0f125SLuiz Capitulino     }
68011d0f125SLuiz Capitulino 
681dc8764f0SLuiz Capitulino     do {
682dc8764f0SLuiz Capitulino         rpid = waitpid(pid, &status, 0);
683dc8764f0SLuiz Capitulino     } while (rpid == -1 && errno == EINTR);
684dc8764f0SLuiz Capitulino     if (rpid == pid && WIFEXITED(status)) {
685dc8764f0SLuiz Capitulino         switch (WEXITSTATUS(status)) {
686dc8764f0SLuiz Capitulino         case SUSPEND_SUPPORTED:
687dc8764f0SLuiz Capitulino             return;
688dc8764f0SLuiz Capitulino         case SUSPEND_NOT_SUPPORTED:
68911d0f125SLuiz Capitulino             error_set(err, QERR_UNSUPPORTED);
690dc8764f0SLuiz Capitulino             return;
691dc8764f0SLuiz Capitulino         default:
692dc8764f0SLuiz Capitulino             goto undef_err;
693dc8764f0SLuiz Capitulino         }
694dc8764f0SLuiz Capitulino     }
69511d0f125SLuiz Capitulino 
696dc8764f0SLuiz Capitulino undef_err:
697dc8764f0SLuiz Capitulino     error_set(err, QERR_UNDEFINED_ERROR);
69811d0f125SLuiz Capitulino }
69911d0f125SLuiz Capitulino 
70011d0f125SLuiz Capitulino static void guest_suspend(const char *pmutils_bin, const char *sysfile_str,
70111d0f125SLuiz Capitulino                           Error **err)
70211d0f125SLuiz Capitulino {
70311d0f125SLuiz Capitulino     char *pmutils_path;
704dc8764f0SLuiz Capitulino     pid_t rpid, pid;
705dc8764f0SLuiz Capitulino     int status;
70611d0f125SLuiz Capitulino 
70711d0f125SLuiz Capitulino     pmutils_path = g_find_program_in_path(pmutils_bin);
70811d0f125SLuiz Capitulino 
70911d0f125SLuiz Capitulino     pid = fork();
71011d0f125SLuiz Capitulino     if (pid == 0) {
71111d0f125SLuiz Capitulino         /* child */
71211d0f125SLuiz Capitulino         int fd;
71311d0f125SLuiz Capitulino 
71411d0f125SLuiz Capitulino         setsid();
71511d0f125SLuiz Capitulino         reopen_fd_to_null(0);
71611d0f125SLuiz Capitulino         reopen_fd_to_null(1);
71711d0f125SLuiz Capitulino         reopen_fd_to_null(2);
71811d0f125SLuiz Capitulino 
71911d0f125SLuiz Capitulino         if (pmutils_path) {
72011d0f125SLuiz Capitulino             execle(pmutils_path, pmutils_bin, NULL, environ);
72111d0f125SLuiz Capitulino         }
72211d0f125SLuiz Capitulino 
72311d0f125SLuiz Capitulino         /*
72411d0f125SLuiz Capitulino          * If we get here either pm-utils is not installed or execle() has
72511d0f125SLuiz Capitulino          * failed. Let's try the manual method if the caller wants it.
72611d0f125SLuiz Capitulino          */
72711d0f125SLuiz Capitulino 
72811d0f125SLuiz Capitulino         if (!sysfile_str) {
72911d0f125SLuiz Capitulino             _exit(EXIT_FAILURE);
73011d0f125SLuiz Capitulino         }
73111d0f125SLuiz Capitulino 
73211d0f125SLuiz Capitulino         fd = open(LINUX_SYS_STATE_FILE, O_WRONLY);
73311d0f125SLuiz Capitulino         if (fd < 0) {
73411d0f125SLuiz Capitulino             _exit(EXIT_FAILURE);
73511d0f125SLuiz Capitulino         }
73611d0f125SLuiz Capitulino 
73711d0f125SLuiz Capitulino         if (write(fd, sysfile_str, strlen(sysfile_str)) < 0) {
73811d0f125SLuiz Capitulino             _exit(EXIT_FAILURE);
73911d0f125SLuiz Capitulino         }
74011d0f125SLuiz Capitulino 
74111d0f125SLuiz Capitulino         _exit(EXIT_SUCCESS);
74211d0f125SLuiz Capitulino     }
74311d0f125SLuiz Capitulino 
74411d0f125SLuiz Capitulino     g_free(pmutils_path);
74511d0f125SLuiz Capitulino 
74611d0f125SLuiz Capitulino     if (pid < 0) {
747dc8764f0SLuiz Capitulino         goto exit_err;
748dc8764f0SLuiz Capitulino     }
749dc8764f0SLuiz Capitulino 
750dc8764f0SLuiz Capitulino     do {
751dc8764f0SLuiz Capitulino         rpid = waitpid(pid, &status, 0);
752dc8764f0SLuiz Capitulino     } while (rpid == -1 && errno == EINTR);
753dc8764f0SLuiz Capitulino     if (rpid == pid && WIFEXITED(status) && !WEXITSTATUS(status)) {
75411d0f125SLuiz Capitulino         return;
75511d0f125SLuiz Capitulino     }
756dc8764f0SLuiz Capitulino 
757dc8764f0SLuiz Capitulino exit_err:
758dc8764f0SLuiz Capitulino     error_set(err, QERR_UNDEFINED_ERROR);
75911d0f125SLuiz Capitulino }
76011d0f125SLuiz Capitulino 
76111d0f125SLuiz Capitulino void qmp_guest_suspend_disk(Error **err)
76211d0f125SLuiz Capitulino {
76311d0f125SLuiz Capitulino     bios_supports_mode("pm-is-supported", "--hibernate", "disk", err);
76411d0f125SLuiz Capitulino     if (error_is_set(err)) {
76511d0f125SLuiz Capitulino         return;
76611d0f125SLuiz Capitulino     }
76711d0f125SLuiz Capitulino 
76811d0f125SLuiz Capitulino     guest_suspend("pm-hibernate", "disk", err);
76911d0f125SLuiz Capitulino }
77011d0f125SLuiz Capitulino 
771fbf42210SLuiz Capitulino void qmp_guest_suspend_ram(Error **err)
772fbf42210SLuiz Capitulino {
773fbf42210SLuiz Capitulino     bios_supports_mode("pm-is-supported", "--suspend", "mem", err);
774fbf42210SLuiz Capitulino     if (error_is_set(err)) {
775fbf42210SLuiz Capitulino         return;
776fbf42210SLuiz Capitulino     }
777fbf42210SLuiz Capitulino 
778fbf42210SLuiz Capitulino     guest_suspend("pm-suspend", "mem", err);
779fbf42210SLuiz Capitulino }
780fbf42210SLuiz Capitulino 
78195f4f404SLuiz Capitulino void qmp_guest_suspend_hybrid(Error **err)
78295f4f404SLuiz Capitulino {
78395f4f404SLuiz Capitulino     bios_supports_mode("pm-is-supported", "--suspend-hybrid", NULL, err);
78495f4f404SLuiz Capitulino     if (error_is_set(err)) {
78595f4f404SLuiz Capitulino         return;
78695f4f404SLuiz Capitulino     }
78795f4f404SLuiz Capitulino 
78895f4f404SLuiz Capitulino     guest_suspend("pm-suspend-hybrid", NULL, err);
78995f4f404SLuiz Capitulino }
79095f4f404SLuiz Capitulino 
7913424fc9fSMichal Privoznik static GuestNetworkInterfaceList *
7923424fc9fSMichal Privoznik guest_find_interface(GuestNetworkInterfaceList *head,
7933424fc9fSMichal Privoznik                      const char *name)
7943424fc9fSMichal Privoznik {
7953424fc9fSMichal Privoznik     for (; head; head = head->next) {
7963424fc9fSMichal Privoznik         if (strcmp(head->value->name, name) == 0) {
7973424fc9fSMichal Privoznik             break;
7983424fc9fSMichal Privoznik         }
7993424fc9fSMichal Privoznik     }
8003424fc9fSMichal Privoznik 
8013424fc9fSMichal Privoznik     return head;
8023424fc9fSMichal Privoznik }
8033424fc9fSMichal Privoznik 
8043424fc9fSMichal Privoznik /*
8053424fc9fSMichal Privoznik  * Build information about guest interfaces
8063424fc9fSMichal Privoznik  */
8073424fc9fSMichal Privoznik GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp)
8083424fc9fSMichal Privoznik {
8093424fc9fSMichal Privoznik     GuestNetworkInterfaceList *head = NULL, *cur_item = NULL;
8103424fc9fSMichal Privoznik     struct ifaddrs *ifap, *ifa;
8113424fc9fSMichal Privoznik     char err_msg[512];
8123424fc9fSMichal Privoznik 
8133424fc9fSMichal Privoznik     if (getifaddrs(&ifap) < 0) {
8143424fc9fSMichal Privoznik         snprintf(err_msg, sizeof(err_msg),
8153424fc9fSMichal Privoznik                  "getifaddrs failed: %s", strerror(errno));
8163424fc9fSMichal Privoznik         error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg);
8173424fc9fSMichal Privoznik         goto error;
8183424fc9fSMichal Privoznik     }
8193424fc9fSMichal Privoznik 
8203424fc9fSMichal Privoznik     for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
8213424fc9fSMichal Privoznik         GuestNetworkInterfaceList *info;
8223424fc9fSMichal Privoznik         GuestIpAddressList **address_list = NULL, *address_item = NULL;
8233424fc9fSMichal Privoznik         char addr4[INET_ADDRSTRLEN];
8243424fc9fSMichal Privoznik         char addr6[INET6_ADDRSTRLEN];
8253424fc9fSMichal Privoznik         int sock;
8263424fc9fSMichal Privoznik         struct ifreq ifr;
8273424fc9fSMichal Privoznik         unsigned char *mac_addr;
8283424fc9fSMichal Privoznik         void *p;
8293424fc9fSMichal Privoznik 
8303424fc9fSMichal Privoznik         g_debug("Processing %s interface", ifa->ifa_name);
8313424fc9fSMichal Privoznik 
8323424fc9fSMichal Privoznik         info = guest_find_interface(head, ifa->ifa_name);
8333424fc9fSMichal Privoznik 
8343424fc9fSMichal Privoznik         if (!info) {
8353424fc9fSMichal Privoznik             info = g_malloc0(sizeof(*info));
8363424fc9fSMichal Privoznik             info->value = g_malloc0(sizeof(*info->value));
8373424fc9fSMichal Privoznik             info->value->name = g_strdup(ifa->ifa_name);
8383424fc9fSMichal Privoznik 
8393424fc9fSMichal Privoznik             if (!cur_item) {
8403424fc9fSMichal Privoznik                 head = cur_item = info;
8413424fc9fSMichal Privoznik             } else {
8423424fc9fSMichal Privoznik                 cur_item->next = info;
8433424fc9fSMichal Privoznik                 cur_item = info;
8443424fc9fSMichal Privoznik             }
8453424fc9fSMichal Privoznik         }
8463424fc9fSMichal Privoznik 
8473424fc9fSMichal Privoznik         if (!info->value->has_hardware_address &&
8483424fc9fSMichal Privoznik             ifa->ifa_flags & SIOCGIFHWADDR) {
8493424fc9fSMichal Privoznik             /* we haven't obtained HW address yet */
8503424fc9fSMichal Privoznik             sock = socket(PF_INET, SOCK_STREAM, 0);
8513424fc9fSMichal Privoznik             if (sock == -1) {
8523424fc9fSMichal Privoznik                 snprintf(err_msg, sizeof(err_msg),
8533424fc9fSMichal Privoznik                          "failed to create socket: %s", strerror(errno));
8543424fc9fSMichal Privoznik                 error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg);
8553424fc9fSMichal Privoznik                 goto error;
8563424fc9fSMichal Privoznik             }
8573424fc9fSMichal Privoznik 
8583424fc9fSMichal Privoznik             memset(&ifr, 0, sizeof(ifr));
8591ab516edSJim Meyering             pstrcpy(ifr.ifr_name, IF_NAMESIZE, info->value->name);
8603424fc9fSMichal Privoznik             if (ioctl(sock, SIOCGIFHWADDR, &ifr) == -1) {
8613424fc9fSMichal Privoznik                 snprintf(err_msg, sizeof(err_msg),
862a31f0531SJim Meyering                          "failed to get MAC address of %s: %s",
8633424fc9fSMichal Privoznik                          ifa->ifa_name,
8643424fc9fSMichal Privoznik                          strerror(errno));
8653424fc9fSMichal Privoznik                 error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg);
8663424fc9fSMichal Privoznik                 goto error;
8673424fc9fSMichal Privoznik             }
8683424fc9fSMichal Privoznik 
8693424fc9fSMichal Privoznik             mac_addr = (unsigned char *) &ifr.ifr_hwaddr.sa_data;
8703424fc9fSMichal Privoznik 
8713424fc9fSMichal Privoznik             if (asprintf(&info->value->hardware_address,
8723424fc9fSMichal Privoznik                          "%02x:%02x:%02x:%02x:%02x:%02x",
8733424fc9fSMichal Privoznik                          (int) mac_addr[0], (int) mac_addr[1],
8743424fc9fSMichal Privoznik                          (int) mac_addr[2], (int) mac_addr[3],
8753424fc9fSMichal Privoznik                          (int) mac_addr[4], (int) mac_addr[5]) == -1) {
8763424fc9fSMichal Privoznik                 snprintf(err_msg, sizeof(err_msg),
8773424fc9fSMichal Privoznik                          "failed to format MAC: %s", strerror(errno));
8783424fc9fSMichal Privoznik                 error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg);
8793424fc9fSMichal Privoznik                 goto error;
8803424fc9fSMichal Privoznik             }
8813424fc9fSMichal Privoznik 
8823424fc9fSMichal Privoznik             info->value->has_hardware_address = true;
8833424fc9fSMichal Privoznik             close(sock);
8843424fc9fSMichal Privoznik         }
8853424fc9fSMichal Privoznik 
8863424fc9fSMichal Privoznik         if (ifa->ifa_addr &&
8873424fc9fSMichal Privoznik             ifa->ifa_addr->sa_family == AF_INET) {
8883424fc9fSMichal Privoznik             /* interface with IPv4 address */
8893424fc9fSMichal Privoznik             address_item = g_malloc0(sizeof(*address_item));
8903424fc9fSMichal Privoznik             address_item->value = g_malloc0(sizeof(*address_item->value));
8913424fc9fSMichal Privoznik             p = &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
8923424fc9fSMichal Privoznik             if (!inet_ntop(AF_INET, p, addr4, sizeof(addr4))) {
8933424fc9fSMichal Privoznik                 snprintf(err_msg, sizeof(err_msg),
8943424fc9fSMichal Privoznik                          "inet_ntop failed : %s", strerror(errno));
8953424fc9fSMichal Privoznik                 error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg);
8963424fc9fSMichal Privoznik                 goto error;
8973424fc9fSMichal Privoznik             }
8983424fc9fSMichal Privoznik 
8993424fc9fSMichal Privoznik             address_item->value->ip_address = g_strdup(addr4);
9003424fc9fSMichal Privoznik             address_item->value->ip_address_type = GUEST_IP_ADDRESS_TYPE_IPV4;
9013424fc9fSMichal Privoznik 
9023424fc9fSMichal Privoznik             if (ifa->ifa_netmask) {
9033424fc9fSMichal Privoznik                 /* Count the number of set bits in netmask.
9043424fc9fSMichal Privoznik                  * This is safe as '1' and '0' cannot be shuffled in netmask. */
9053424fc9fSMichal Privoznik                 p = &((struct sockaddr_in *)ifa->ifa_netmask)->sin_addr;
9063424fc9fSMichal Privoznik                 address_item->value->prefix = ctpop32(((uint32_t *) p)[0]);
9073424fc9fSMichal Privoznik             }
9083424fc9fSMichal Privoznik         } else if (ifa->ifa_addr &&
9093424fc9fSMichal Privoznik                    ifa->ifa_addr->sa_family == AF_INET6) {
9103424fc9fSMichal Privoznik             /* interface with IPv6 address */
9113424fc9fSMichal Privoznik             address_item = g_malloc0(sizeof(*address_item));
9123424fc9fSMichal Privoznik             address_item->value = g_malloc0(sizeof(*address_item->value));
9133424fc9fSMichal Privoznik             p = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
9143424fc9fSMichal Privoznik             if (!inet_ntop(AF_INET6, p, addr6, sizeof(addr6))) {
9153424fc9fSMichal Privoznik                 snprintf(err_msg, sizeof(err_msg),
9163424fc9fSMichal Privoznik                          "inet_ntop failed : %s", strerror(errno));
9173424fc9fSMichal Privoznik                 error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg);
9183424fc9fSMichal Privoznik                 goto error;
9193424fc9fSMichal Privoznik             }
9203424fc9fSMichal Privoznik 
9213424fc9fSMichal Privoznik             address_item->value->ip_address = g_strdup(addr6);
9223424fc9fSMichal Privoznik             address_item->value->ip_address_type = GUEST_IP_ADDRESS_TYPE_IPV6;
9233424fc9fSMichal Privoznik 
9243424fc9fSMichal Privoznik             if (ifa->ifa_netmask) {
9253424fc9fSMichal Privoznik                 /* Count the number of set bits in netmask.
9263424fc9fSMichal Privoznik                  * This is safe as '1' and '0' cannot be shuffled in netmask. */
9273424fc9fSMichal Privoznik                 p = &((struct sockaddr_in6 *)ifa->ifa_netmask)->sin6_addr;
9283424fc9fSMichal Privoznik                 address_item->value->prefix =
9293424fc9fSMichal Privoznik                     ctpop32(((uint32_t *) p)[0]) +
9303424fc9fSMichal Privoznik                     ctpop32(((uint32_t *) p)[1]) +
9313424fc9fSMichal Privoznik                     ctpop32(((uint32_t *) p)[2]) +
9323424fc9fSMichal Privoznik                     ctpop32(((uint32_t *) p)[3]);
9333424fc9fSMichal Privoznik             }
9343424fc9fSMichal Privoznik         }
9353424fc9fSMichal Privoznik 
9363424fc9fSMichal Privoznik         if (!address_item) {
9373424fc9fSMichal Privoznik             continue;
9383424fc9fSMichal Privoznik         }
9393424fc9fSMichal Privoznik 
9403424fc9fSMichal Privoznik         address_list = &info->value->ip_addresses;
9413424fc9fSMichal Privoznik 
9423424fc9fSMichal Privoznik         while (*address_list && (*address_list)->next) {
9433424fc9fSMichal Privoznik             address_list = &(*address_list)->next;
9443424fc9fSMichal Privoznik         }
9453424fc9fSMichal Privoznik 
9463424fc9fSMichal Privoznik         if (!*address_list) {
9473424fc9fSMichal Privoznik             *address_list = address_item;
9483424fc9fSMichal Privoznik         } else {
9493424fc9fSMichal Privoznik             (*address_list)->next = address_item;
9503424fc9fSMichal Privoznik         }
9513424fc9fSMichal Privoznik 
9523424fc9fSMichal Privoznik         info->value->has_ip_addresses = true;
9533424fc9fSMichal Privoznik 
9543424fc9fSMichal Privoznik 
9553424fc9fSMichal Privoznik     }
9563424fc9fSMichal Privoznik 
9573424fc9fSMichal Privoznik     freeifaddrs(ifap);
9583424fc9fSMichal Privoznik     return head;
9593424fc9fSMichal Privoznik 
9603424fc9fSMichal Privoznik error:
9613424fc9fSMichal Privoznik     freeifaddrs(ifap);
9623424fc9fSMichal Privoznik     qapi_free_GuestNetworkInterfaceList(head);
9633424fc9fSMichal Privoznik     return NULL;
9643424fc9fSMichal Privoznik }
9653424fc9fSMichal Privoznik 
966e72c3f2eSMichael Roth #else /* defined(__linux__) */
967e72c3f2eSMichael Roth 
968e72c3f2eSMichael Roth void qmp_guest_suspend_disk(Error **err)
969e72c3f2eSMichael Roth {
970e72c3f2eSMichael Roth     error_set(err, QERR_UNSUPPORTED);
971e72c3f2eSMichael Roth }
972e72c3f2eSMichael Roth 
973e72c3f2eSMichael Roth void qmp_guest_suspend_ram(Error **err)
974e72c3f2eSMichael Roth {
975e72c3f2eSMichael Roth     error_set(err, QERR_UNSUPPORTED);
976e72c3f2eSMichael Roth }
977e72c3f2eSMichael Roth 
978e72c3f2eSMichael Roth void qmp_guest_suspend_hybrid(Error **err)
979e72c3f2eSMichael Roth {
980e72c3f2eSMichael Roth     error_set(err, QERR_UNSUPPORTED);
981e72c3f2eSMichael Roth }
982e72c3f2eSMichael Roth 
983e72c3f2eSMichael Roth GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp)
984e72c3f2eSMichael Roth {
985e72c3f2eSMichael Roth     error_set(errp, QERR_UNSUPPORTED);
986e72c3f2eSMichael Roth     return NULL;
987e72c3f2eSMichael Roth }
988e72c3f2eSMichael Roth 
989e72c3f2eSMichael Roth #endif
990e72c3f2eSMichael Roth 
991d35d4cb5SMichael Roth #if !defined(CONFIG_FSFREEZE)
992d35d4cb5SMichael Roth 
993d35d4cb5SMichael Roth GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **err)
994d35d4cb5SMichael Roth {
995d35d4cb5SMichael Roth     error_set(err, QERR_UNSUPPORTED);
996d35d4cb5SMichael Roth 
997d35d4cb5SMichael Roth     return 0;
998d35d4cb5SMichael Roth }
999d35d4cb5SMichael Roth 
1000d35d4cb5SMichael Roth int64_t qmp_guest_fsfreeze_freeze(Error **err)
1001d35d4cb5SMichael Roth {
1002d35d4cb5SMichael Roth     error_set(err, QERR_UNSUPPORTED);
1003d35d4cb5SMichael Roth 
1004d35d4cb5SMichael Roth     return 0;
1005d35d4cb5SMichael Roth }
1006d35d4cb5SMichael Roth 
1007d35d4cb5SMichael Roth int64_t qmp_guest_fsfreeze_thaw(Error **err)
1008d35d4cb5SMichael Roth {
1009d35d4cb5SMichael Roth     error_set(err, QERR_UNSUPPORTED);
1010d35d4cb5SMichael Roth 
1011d35d4cb5SMichael Roth     return 0;
1012d35d4cb5SMichael Roth }
1013eab5fd59SPaolo Bonzini #endif /* CONFIG_FSFREEZE */
1014d35d4cb5SMichael Roth 
1015eab5fd59SPaolo Bonzini #if !defined(CONFIG_FSTRIM)
1016eab5fd59SPaolo Bonzini void qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **err)
1017eab5fd59SPaolo Bonzini {
1018eab5fd59SPaolo Bonzini     error_set(err, QERR_UNSUPPORTED);
1019eab5fd59SPaolo Bonzini }
1020d35d4cb5SMichael Roth #endif
1021d35d4cb5SMichael Roth 
1022c216e5adSMichael Roth /* register init/cleanup routines for stateful command groups */
1023c216e5adSMichael Roth void ga_command_state_init(GAState *s, GACommandState *cs)
1024c216e5adSMichael Roth {
1025c216e5adSMichael Roth #if defined(CONFIG_FSFREEZE)
1026f22d85e9SMichael Roth     ga_command_state_add(cs, NULL, guest_fsfreeze_cleanup);
1027c216e5adSMichael Roth #endif
1028c216e5adSMichael Roth     ga_command_state_add(cs, guest_file_init, NULL);
1029c216e5adSMichael Roth }
1030