xref: /openbmc/qemu/qga/commands-posix.c (revision 39097daf)
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 
49d220a6dfSLuiz Capitulino static void ga_wait_child(pid_t pid, int *status, Error **err)
50d220a6dfSLuiz Capitulino {
51d220a6dfSLuiz Capitulino     pid_t rpid;
52d220a6dfSLuiz Capitulino 
53d220a6dfSLuiz Capitulino     *status = 0;
54d220a6dfSLuiz Capitulino 
55d220a6dfSLuiz Capitulino     do {
56d220a6dfSLuiz Capitulino         rpid = waitpid(pid, status, 0);
57d220a6dfSLuiz Capitulino     } while (rpid == -1 && errno == EINTR);
58d220a6dfSLuiz Capitulino 
59d220a6dfSLuiz Capitulino     if (rpid == -1) {
60d220a6dfSLuiz Capitulino         error_setg_errno(err, errno, "failed to wait for child (pid: %d)", pid);
61d220a6dfSLuiz Capitulino         return;
62d220a6dfSLuiz Capitulino     }
63d220a6dfSLuiz Capitulino 
64d220a6dfSLuiz Capitulino     g_assert(rpid == pid);
65d220a6dfSLuiz Capitulino }
66d220a6dfSLuiz Capitulino 
67c216e5adSMichael Roth void qmp_guest_shutdown(bool has_mode, const char *mode, Error **err)
68c216e5adSMichael Roth {
69c216e5adSMichael Roth     const char *shutdown_flag;
70d220a6dfSLuiz Capitulino     Error *local_err = NULL;
71d220a6dfSLuiz 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 {
82d220a6dfSLuiz Capitulino         error_setg(err,
83d220a6dfSLuiz 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) {
99d220a6dfSLuiz Capitulino         error_setg_errno(err, errno, "failed to create child process");
100d5dd3498SLuiz Capitulino         return;
101d5dd3498SLuiz Capitulino     }
102d5dd3498SLuiz Capitulino 
103d220a6dfSLuiz Capitulino     ga_wait_child(pid, &status, &local_err);
104d220a6dfSLuiz Capitulino     if (error_is_set(&local_err)) {
105d220a6dfSLuiz Capitulino         error_propagate(err, local_err);
106d220a6dfSLuiz Capitulino         return;
107d220a6dfSLuiz Capitulino     }
108d220a6dfSLuiz Capitulino 
109d220a6dfSLuiz Capitulino     if (!WIFEXITED(status)) {
110d220a6dfSLuiz Capitulino         error_setg(err, "child process has terminated abnormally");
111d220a6dfSLuiz Capitulino         return;
112d220a6dfSLuiz Capitulino     }
113d220a6dfSLuiz Capitulino 
114d220a6dfSLuiz Capitulino     if (WEXITSTATUS(status)) {
115d220a6dfSLuiz Capitulino         error_setg(err, "child process has failed to shutdown");
116d220a6dfSLuiz Capitulino         return;
117d220a6dfSLuiz Capitulino     }
118d220a6dfSLuiz Capitulino 
119d220a6dfSLuiz 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 
132*39097dafSMichael Roth static int64_t guest_file_handle_add(FILE *fh, Error **errp)
133c216e5adSMichael Roth {
134c216e5adSMichael Roth     GuestFileHandle *gfh;
135*39097dafSMichael Roth     int64_t handle;
136*39097dafSMichael Roth 
137*39097dafSMichael Roth     handle = ga_get_fd_handle(ga_state, errp);
138*39097dafSMichael Roth     if (error_is_set(errp)) {
139*39097dafSMichael Roth         return 0;
140*39097dafSMichael Roth     }
141c216e5adSMichael Roth 
142c216e5adSMichael Roth     gfh = g_malloc0(sizeof(GuestFileHandle));
143*39097dafSMichael Roth     gfh->id = handle;
144c216e5adSMichael Roth     gfh->fh = fh;
145c216e5adSMichael Roth     QTAILQ_INSERT_TAIL(&guest_file_state.filehandles, gfh, next);
146*39097dafSMichael Roth 
147*39097dafSMichael Roth     return handle;
148c216e5adSMichael Roth }
149c216e5adSMichael Roth 
150a9de6d01SLuiz Capitulino static GuestFileHandle *guest_file_handle_find(int64_t id, Error **err)
151c216e5adSMichael Roth {
152c216e5adSMichael Roth     GuestFileHandle *gfh;
153c216e5adSMichael Roth 
154c216e5adSMichael Roth     QTAILQ_FOREACH(gfh, &guest_file_state.filehandles, next)
155c216e5adSMichael Roth     {
156c216e5adSMichael Roth         if (gfh->id == id) {
157c216e5adSMichael Roth             return gfh;
158c216e5adSMichael Roth         }
159c216e5adSMichael Roth     }
160c216e5adSMichael Roth 
161a9de6d01SLuiz Capitulino     error_setg(err, "handle '%" PRId64 "' has not been found", id);
162c216e5adSMichael Roth     return NULL;
163c216e5adSMichael Roth }
164c216e5adSMichael Roth 
165c216e5adSMichael Roth int64_t qmp_guest_file_open(const char *path, bool has_mode, const char *mode, Error **err)
166c216e5adSMichael Roth {
167c216e5adSMichael Roth     FILE *fh;
168c216e5adSMichael Roth     int fd;
169*39097dafSMichael Roth     int64_t ret = -1, handle;
170c216e5adSMichael Roth 
171c216e5adSMichael Roth     if (!has_mode) {
172c216e5adSMichael Roth         mode = "r";
173c216e5adSMichael Roth     }
174c216e5adSMichael Roth     slog("guest-file-open called, filepath: %s, mode: %s", path, mode);
175c216e5adSMichael Roth     fh = fopen(path, mode);
176c216e5adSMichael Roth     if (!fh) {
177db3edb66SLuiz Capitulino         error_setg_errno(err, errno, "failed to open file '%s' (mode: '%s')",
178db3edb66SLuiz Capitulino                          path, mode);
179c216e5adSMichael Roth         return -1;
180c216e5adSMichael Roth     }
181c216e5adSMichael Roth 
182c216e5adSMichael Roth     /* set fd non-blocking to avoid common use cases (like reading from a
183c216e5adSMichael Roth      * named pipe) from hanging the agent
184c216e5adSMichael Roth      */
185c216e5adSMichael Roth     fd = fileno(fh);
186c216e5adSMichael Roth     ret = fcntl(fd, F_GETFL);
187c216e5adSMichael Roth     ret = fcntl(fd, F_SETFL, ret | O_NONBLOCK);
188c216e5adSMichael Roth     if (ret == -1) {
189db3edb66SLuiz Capitulino         error_setg_errno(err, errno, "failed to make file '%s' non-blocking",
190db3edb66SLuiz Capitulino                          path);
191c216e5adSMichael Roth         fclose(fh);
192c216e5adSMichael Roth         return -1;
193c216e5adSMichael Roth     }
194c216e5adSMichael Roth 
195*39097dafSMichael Roth     handle = guest_file_handle_add(fh, err);
196*39097dafSMichael Roth     if (error_is_set(err)) {
197*39097dafSMichael Roth         fclose(fh);
198*39097dafSMichael Roth         return -1;
199*39097dafSMichael Roth     }
200*39097dafSMichael Roth 
201*39097dafSMichael Roth     slog("guest-file-open, handle: %d", handle);
202*39097dafSMichael Roth     return handle;
203c216e5adSMichael Roth }
204c216e5adSMichael Roth 
205c216e5adSMichael Roth void qmp_guest_file_close(int64_t handle, Error **err)
206c216e5adSMichael Roth {
207a9de6d01SLuiz Capitulino     GuestFileHandle *gfh = guest_file_handle_find(handle, err);
208c216e5adSMichael Roth     int ret;
209c216e5adSMichael Roth 
210c216e5adSMichael Roth     slog("guest-file-close called, handle: %ld", handle);
211c216e5adSMichael Roth     if (!gfh) {
212c216e5adSMichael Roth         return;
213c216e5adSMichael Roth     }
214c216e5adSMichael Roth 
215c216e5adSMichael Roth     ret = fclose(gfh->fh);
2163ac4b7c5SLuiz Capitulino     if (ret == EOF) {
217db3edb66SLuiz Capitulino         error_setg_errno(err, errno, "failed to close handle");
218c216e5adSMichael Roth         return;
219c216e5adSMichael Roth     }
220c216e5adSMichael Roth 
221c216e5adSMichael Roth     QTAILQ_REMOVE(&guest_file_state.filehandles, gfh, next);
222c216e5adSMichael Roth     g_free(gfh);
223c216e5adSMichael Roth }
224c216e5adSMichael Roth 
225c216e5adSMichael Roth struct GuestFileRead *qmp_guest_file_read(int64_t handle, bool has_count,
226c216e5adSMichael Roth                                           int64_t count, Error **err)
227c216e5adSMichael Roth {
228a9de6d01SLuiz Capitulino     GuestFileHandle *gfh = guest_file_handle_find(handle, err);
229c216e5adSMichael Roth     GuestFileRead *read_data = NULL;
230c216e5adSMichael Roth     guchar *buf;
231c216e5adSMichael Roth     FILE *fh;
232c216e5adSMichael Roth     size_t read_count;
233c216e5adSMichael Roth 
234c216e5adSMichael Roth     if (!gfh) {
235c216e5adSMichael Roth         return NULL;
236c216e5adSMichael Roth     }
237c216e5adSMichael Roth 
238c216e5adSMichael Roth     if (!has_count) {
239c216e5adSMichael Roth         count = QGA_READ_COUNT_DEFAULT;
240c216e5adSMichael Roth     } else if (count < 0) {
241db3edb66SLuiz Capitulino         error_setg(err, "value '%" PRId64 "' is invalid for argument count",
242db3edb66SLuiz Capitulino                    count);
243c216e5adSMichael Roth         return NULL;
244c216e5adSMichael Roth     }
245c216e5adSMichael Roth 
246c216e5adSMichael Roth     fh = gfh->fh;
247c216e5adSMichael Roth     buf = g_malloc0(count+1);
248c216e5adSMichael Roth     read_count = fread(buf, 1, count, fh);
249c216e5adSMichael Roth     if (ferror(fh)) {
250db3edb66SLuiz Capitulino         error_setg_errno(err, errno, "failed to read file");
251c216e5adSMichael Roth         slog("guest-file-read failed, handle: %ld", handle);
252c216e5adSMichael Roth     } else {
253c216e5adSMichael Roth         buf[read_count] = 0;
254c216e5adSMichael Roth         read_data = g_malloc0(sizeof(GuestFileRead));
255c216e5adSMichael Roth         read_data->count = read_count;
256c216e5adSMichael Roth         read_data->eof = feof(fh);
257c216e5adSMichael Roth         if (read_count) {
258c216e5adSMichael Roth             read_data->buf_b64 = g_base64_encode(buf, read_count);
259c216e5adSMichael Roth         }
260c216e5adSMichael Roth     }
261c216e5adSMichael Roth     g_free(buf);
262c216e5adSMichael Roth     clearerr(fh);
263c216e5adSMichael Roth 
264c216e5adSMichael Roth     return read_data;
265c216e5adSMichael Roth }
266c216e5adSMichael Roth 
267c216e5adSMichael Roth GuestFileWrite *qmp_guest_file_write(int64_t handle, const char *buf_b64,
268c216e5adSMichael Roth                                      bool has_count, int64_t count, Error **err)
269c216e5adSMichael Roth {
270c216e5adSMichael Roth     GuestFileWrite *write_data = NULL;
271c216e5adSMichael Roth     guchar *buf;
272c216e5adSMichael Roth     gsize buf_len;
273c216e5adSMichael Roth     int write_count;
274a9de6d01SLuiz Capitulino     GuestFileHandle *gfh = guest_file_handle_find(handle, err);
275c216e5adSMichael Roth     FILE *fh;
276c216e5adSMichael Roth 
277c216e5adSMichael Roth     if (!gfh) {
278c216e5adSMichael Roth         return NULL;
279c216e5adSMichael Roth     }
280c216e5adSMichael Roth 
281c216e5adSMichael Roth     fh = gfh->fh;
282c216e5adSMichael Roth     buf = g_base64_decode(buf_b64, &buf_len);
283c216e5adSMichael Roth 
284c216e5adSMichael Roth     if (!has_count) {
285c216e5adSMichael Roth         count = buf_len;
286c216e5adSMichael Roth     } else if (count < 0 || count > buf_len) {
287db3edb66SLuiz Capitulino         error_setg(err, "value '%" PRId64 "' is invalid for argument count",
288db3edb66SLuiz Capitulino                    count);
289c216e5adSMichael Roth         g_free(buf);
290c216e5adSMichael Roth         return NULL;
291c216e5adSMichael Roth     }
292c216e5adSMichael Roth 
293c216e5adSMichael Roth     write_count = fwrite(buf, 1, count, fh);
294c216e5adSMichael Roth     if (ferror(fh)) {
295db3edb66SLuiz Capitulino         error_setg_errno(err, errno, "failed to write to file");
296c216e5adSMichael Roth         slog("guest-file-write failed, handle: %ld", handle);
297c216e5adSMichael Roth     } else {
298c216e5adSMichael Roth         write_data = g_malloc0(sizeof(GuestFileWrite));
299c216e5adSMichael Roth         write_data->count = write_count;
300c216e5adSMichael Roth         write_data->eof = feof(fh);
301c216e5adSMichael Roth     }
302c216e5adSMichael Roth     g_free(buf);
303c216e5adSMichael Roth     clearerr(fh);
304c216e5adSMichael Roth 
305c216e5adSMichael Roth     return write_data;
306c216e5adSMichael Roth }
307c216e5adSMichael Roth 
308c216e5adSMichael Roth struct GuestFileSeek *qmp_guest_file_seek(int64_t handle, int64_t offset,
309c216e5adSMichael Roth                                           int64_t whence, Error **err)
310c216e5adSMichael Roth {
311a9de6d01SLuiz Capitulino     GuestFileHandle *gfh = guest_file_handle_find(handle, err);
312c216e5adSMichael Roth     GuestFileSeek *seek_data = NULL;
313c216e5adSMichael Roth     FILE *fh;
314c216e5adSMichael Roth     int ret;
315c216e5adSMichael Roth 
316c216e5adSMichael Roth     if (!gfh) {
317c216e5adSMichael Roth         return NULL;
318c216e5adSMichael Roth     }
319c216e5adSMichael Roth 
320c216e5adSMichael Roth     fh = gfh->fh;
321c216e5adSMichael Roth     ret = fseek(fh, offset, whence);
322c216e5adSMichael Roth     if (ret == -1) {
323db3edb66SLuiz Capitulino         error_setg_errno(err, errno, "failed to seek file");
324c216e5adSMichael Roth     } else {
325c216e5adSMichael Roth         seek_data = g_malloc0(sizeof(GuestFileRead));
326c216e5adSMichael Roth         seek_data->position = ftell(fh);
327c216e5adSMichael Roth         seek_data->eof = feof(fh);
328c216e5adSMichael Roth     }
329c216e5adSMichael Roth     clearerr(fh);
330c216e5adSMichael Roth 
331c216e5adSMichael Roth     return seek_data;
332c216e5adSMichael Roth }
333c216e5adSMichael Roth 
334c216e5adSMichael Roth void qmp_guest_file_flush(int64_t handle, Error **err)
335c216e5adSMichael Roth {
336a9de6d01SLuiz Capitulino     GuestFileHandle *gfh = guest_file_handle_find(handle, err);
337c216e5adSMichael Roth     FILE *fh;
338c216e5adSMichael Roth     int ret;
339c216e5adSMichael Roth 
340c216e5adSMichael Roth     if (!gfh) {
341c216e5adSMichael Roth         return;
342c216e5adSMichael Roth     }
343c216e5adSMichael Roth 
344c216e5adSMichael Roth     fh = gfh->fh;
345c216e5adSMichael Roth     ret = fflush(fh);
346c216e5adSMichael Roth     if (ret == EOF) {
347db3edb66SLuiz Capitulino         error_setg_errno(err, errno, "failed to flush file");
348c216e5adSMichael Roth     }
349c216e5adSMichael Roth }
350c216e5adSMichael Roth 
351c216e5adSMichael Roth static void guest_file_init(void)
352c216e5adSMichael Roth {
353c216e5adSMichael Roth     QTAILQ_INIT(&guest_file_state.filehandles);
354c216e5adSMichael Roth }
355c216e5adSMichael Roth 
356e72c3f2eSMichael Roth /* linux-specific implementations. avoid this if at all possible. */
357e72c3f2eSMichael Roth #if defined(__linux__)
358e72c3f2eSMichael Roth 
359eab5fd59SPaolo Bonzini #if defined(CONFIG_FSFREEZE) || defined(CONFIG_FSTRIM)
360af02203fSPaolo Bonzini typedef struct FsMount {
361c216e5adSMichael Roth     char *dirname;
362c216e5adSMichael Roth     char *devtype;
363af02203fSPaolo Bonzini     QTAILQ_ENTRY(FsMount) next;
364af02203fSPaolo Bonzini } FsMount;
365c216e5adSMichael Roth 
366af02203fSPaolo Bonzini typedef QTAILQ_HEAD(, FsMount) FsMountList;
3679e8aded4SMichael Roth 
368af02203fSPaolo Bonzini static void free_fs_mount_list(FsMountList *mounts)
369c216e5adSMichael Roth {
370af02203fSPaolo Bonzini      FsMount *mount, *temp;
371c216e5adSMichael Roth 
3729e8aded4SMichael Roth      if (!mounts) {
3739e8aded4SMichael Roth          return;
3749e8aded4SMichael Roth      }
3759e8aded4SMichael Roth 
3769e8aded4SMichael Roth      QTAILQ_FOREACH_SAFE(mount, mounts, next, temp) {
3779e8aded4SMichael Roth          QTAILQ_REMOVE(mounts, mount, next);
378c216e5adSMichael Roth          g_free(mount->dirname);
379c216e5adSMichael Roth          g_free(mount->devtype);
380c216e5adSMichael Roth          g_free(mount);
381c216e5adSMichael Roth      }
3829e8aded4SMichael Roth }
3839e8aded4SMichael Roth 
3849e8aded4SMichael Roth /*
3859e8aded4SMichael Roth  * Walk the mount table and build a list of local file systems
3869e8aded4SMichael Roth  */
387261551d1SLuiz Capitulino static void build_fs_mount_list(FsMountList *mounts, Error **err)
3889e8aded4SMichael Roth {
3899e8aded4SMichael Roth     struct mntent *ment;
390af02203fSPaolo Bonzini     FsMount *mount;
3919e2fa418SMichael Roth     char const *mtab = "/proc/self/mounts";
3929e8aded4SMichael Roth     FILE *fp;
393c216e5adSMichael Roth 
394c216e5adSMichael Roth     fp = setmntent(mtab, "r");
395c216e5adSMichael Roth     if (!fp) {
396261551d1SLuiz Capitulino         error_setg(err, "failed to open mtab file: '%s'", mtab);
397261551d1SLuiz Capitulino         return;
398c216e5adSMichael Roth     }
399c216e5adSMichael Roth 
400c216e5adSMichael Roth     while ((ment = getmntent(fp))) {
401c216e5adSMichael Roth         /*
402c216e5adSMichael Roth          * An entry which device name doesn't start with a '/' is
403c216e5adSMichael Roth          * either a dummy file system or a network file system.
404c216e5adSMichael Roth          * Add special handling for smbfs and cifs as is done by
405c216e5adSMichael Roth          * coreutils as well.
406c216e5adSMichael Roth          */
407c216e5adSMichael Roth         if ((ment->mnt_fsname[0] != '/') ||
408c216e5adSMichael Roth             (strcmp(ment->mnt_type, "smbfs") == 0) ||
409c216e5adSMichael Roth             (strcmp(ment->mnt_type, "cifs") == 0)) {
410c216e5adSMichael Roth             continue;
411c216e5adSMichael Roth         }
412c216e5adSMichael Roth 
413af02203fSPaolo Bonzini         mount = g_malloc0(sizeof(FsMount));
414c216e5adSMichael Roth         mount->dirname = g_strdup(ment->mnt_dir);
415c216e5adSMichael Roth         mount->devtype = g_strdup(ment->mnt_type);
416c216e5adSMichael Roth 
4179e8aded4SMichael Roth         QTAILQ_INSERT_TAIL(mounts, mount, next);
418c216e5adSMichael Roth     }
419c216e5adSMichael Roth 
420c216e5adSMichael Roth     endmntent(fp);
421c216e5adSMichael Roth }
422eab5fd59SPaolo Bonzini #endif
423eab5fd59SPaolo Bonzini 
424eab5fd59SPaolo Bonzini #if defined(CONFIG_FSFREEZE)
425c216e5adSMichael Roth 
426ec0f694cSTomoki Sekiyama typedef enum {
427ec0f694cSTomoki Sekiyama     FSFREEZE_HOOK_THAW = 0,
428ec0f694cSTomoki Sekiyama     FSFREEZE_HOOK_FREEZE,
429ec0f694cSTomoki Sekiyama } FsfreezeHookArg;
430ec0f694cSTomoki Sekiyama 
431ec0f694cSTomoki Sekiyama const char *fsfreeze_hook_arg_string[] = {
432ec0f694cSTomoki Sekiyama     "thaw",
433ec0f694cSTomoki Sekiyama     "freeze",
434ec0f694cSTomoki Sekiyama };
435ec0f694cSTomoki Sekiyama 
436ec0f694cSTomoki Sekiyama static void execute_fsfreeze_hook(FsfreezeHookArg arg, Error **err)
437ec0f694cSTomoki Sekiyama {
438ec0f694cSTomoki Sekiyama     int status;
439ec0f694cSTomoki Sekiyama     pid_t pid;
440ec0f694cSTomoki Sekiyama     const char *hook;
441ec0f694cSTomoki Sekiyama     const char *arg_str = fsfreeze_hook_arg_string[arg];
442ec0f694cSTomoki Sekiyama     Error *local_err = NULL;
443ec0f694cSTomoki Sekiyama 
444ec0f694cSTomoki Sekiyama     hook = ga_fsfreeze_hook(ga_state);
445ec0f694cSTomoki Sekiyama     if (!hook) {
446ec0f694cSTomoki Sekiyama         return;
447ec0f694cSTomoki Sekiyama     }
448ec0f694cSTomoki Sekiyama     if (access(hook, X_OK) != 0) {
449ec0f694cSTomoki Sekiyama         error_setg_errno(err, errno, "can't access fsfreeze hook '%s'", hook);
450ec0f694cSTomoki Sekiyama         return;
451ec0f694cSTomoki Sekiyama     }
452ec0f694cSTomoki Sekiyama 
453ec0f694cSTomoki Sekiyama     slog("executing fsfreeze hook with arg '%s'", arg_str);
454ec0f694cSTomoki Sekiyama     pid = fork();
455ec0f694cSTomoki Sekiyama     if (pid == 0) {
456ec0f694cSTomoki Sekiyama         setsid();
457ec0f694cSTomoki Sekiyama         reopen_fd_to_null(0);
458ec0f694cSTomoki Sekiyama         reopen_fd_to_null(1);
459ec0f694cSTomoki Sekiyama         reopen_fd_to_null(2);
460ec0f694cSTomoki Sekiyama 
461ec0f694cSTomoki Sekiyama         execle(hook, hook, arg_str, NULL, environ);
462ec0f694cSTomoki Sekiyama         _exit(EXIT_FAILURE);
463ec0f694cSTomoki Sekiyama     } else if (pid < 0) {
464ec0f694cSTomoki Sekiyama         error_setg_errno(err, errno, "failed to create child process");
465ec0f694cSTomoki Sekiyama         return;
466ec0f694cSTomoki Sekiyama     }
467ec0f694cSTomoki Sekiyama 
468ec0f694cSTomoki Sekiyama     ga_wait_child(pid, &status, &local_err);
469ec0f694cSTomoki Sekiyama     if (error_is_set(&local_err)) {
470ec0f694cSTomoki Sekiyama         error_propagate(err, local_err);
471ec0f694cSTomoki Sekiyama         return;
472ec0f694cSTomoki Sekiyama     }
473ec0f694cSTomoki Sekiyama 
474ec0f694cSTomoki Sekiyama     if (!WIFEXITED(status)) {
475ec0f694cSTomoki Sekiyama         error_setg(err, "fsfreeze hook has terminated abnormally");
476ec0f694cSTomoki Sekiyama         return;
477ec0f694cSTomoki Sekiyama     }
478ec0f694cSTomoki Sekiyama 
479ec0f694cSTomoki Sekiyama     status = WEXITSTATUS(status);
480ec0f694cSTomoki Sekiyama     if (status) {
481ec0f694cSTomoki Sekiyama         error_setg(err, "fsfreeze hook has failed with status %d", status);
482ec0f694cSTomoki Sekiyama         return;
483ec0f694cSTomoki Sekiyama     }
484ec0f694cSTomoki Sekiyama }
485ec0f694cSTomoki Sekiyama 
486c216e5adSMichael Roth /*
487c216e5adSMichael Roth  * Return status of freeze/thaw
488c216e5adSMichael Roth  */
489c216e5adSMichael Roth GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **err)
490c216e5adSMichael Roth {
491f22d85e9SMichael Roth     if (ga_is_frozen(ga_state)) {
492f22d85e9SMichael Roth         return GUEST_FSFREEZE_STATUS_FROZEN;
493f22d85e9SMichael Roth     }
494f22d85e9SMichael Roth 
495f22d85e9SMichael Roth     return GUEST_FSFREEZE_STATUS_THAWED;
496c216e5adSMichael Roth }
497c216e5adSMichael Roth 
498c216e5adSMichael Roth /*
499c216e5adSMichael Roth  * Walk list of mounted file systems in the guest, and freeze the ones which
500c216e5adSMichael Roth  * are real local file systems.
501c216e5adSMichael Roth  */
502c216e5adSMichael Roth int64_t qmp_guest_fsfreeze_freeze(Error **err)
503c216e5adSMichael Roth {
504c216e5adSMichael Roth     int ret = 0, i = 0;
505af02203fSPaolo Bonzini     FsMountList mounts;
506af02203fSPaolo Bonzini     struct FsMount *mount;
507261551d1SLuiz Capitulino     Error *local_err = NULL;
508c216e5adSMichael Roth     int fd;
509c216e5adSMichael Roth 
510c216e5adSMichael Roth     slog("guest-fsfreeze called");
511c216e5adSMichael Roth 
512ec0f694cSTomoki Sekiyama     execute_fsfreeze_hook(FSFREEZE_HOOK_FREEZE, &local_err);
513ec0f694cSTomoki Sekiyama     if (error_is_set(&local_err)) {
514ec0f694cSTomoki Sekiyama         error_propagate(err, local_err);
515ec0f694cSTomoki Sekiyama         return -1;
516ec0f694cSTomoki Sekiyama     }
517ec0f694cSTomoki Sekiyama 
5189e8aded4SMichael Roth     QTAILQ_INIT(&mounts);
519261551d1SLuiz Capitulino     build_fs_mount_list(&mounts, &local_err);
520261551d1SLuiz Capitulino     if (error_is_set(&local_err)) {
521261551d1SLuiz Capitulino         error_propagate(err, local_err);
522261551d1SLuiz Capitulino         return -1;
523c216e5adSMichael Roth     }
524c216e5adSMichael Roth 
525c216e5adSMichael Roth     /* cannot risk guest agent blocking itself on a write in this state */
526f22d85e9SMichael Roth     ga_set_frozen(ga_state);
527c216e5adSMichael Roth 
5289e8aded4SMichael Roth     QTAILQ_FOREACH(mount, &mounts, next) {
529c216e5adSMichael Roth         fd = qemu_open(mount->dirname, O_RDONLY);
530c216e5adSMichael Roth         if (fd == -1) {
531617fbbc1SLuiz Capitulino             error_setg_errno(err, errno, "failed to open %s", mount->dirname);
532c216e5adSMichael Roth             goto error;
533c216e5adSMichael Roth         }
534c216e5adSMichael Roth 
535c216e5adSMichael Roth         /* we try to cull filesytems we know won't work in advance, but other
536c216e5adSMichael Roth          * filesytems may not implement fsfreeze for less obvious reasons.
5379e8aded4SMichael Roth          * these will report EOPNOTSUPP. we simply ignore these when tallying
5389e8aded4SMichael Roth          * the number of frozen filesystems.
5399e8aded4SMichael Roth          *
5409e8aded4SMichael Roth          * any other error means a failure to freeze a filesystem we
5419e8aded4SMichael Roth          * expect to be freezable, so return an error in those cases
5429e8aded4SMichael Roth          * and return system to thawed state.
543c216e5adSMichael Roth          */
544c216e5adSMichael Roth         ret = ioctl(fd, FIFREEZE);
5459e8aded4SMichael Roth         if (ret == -1) {
5469e8aded4SMichael Roth             if (errno != EOPNOTSUPP) {
547617fbbc1SLuiz Capitulino                 error_setg_errno(err, errno, "failed to freeze %s",
548617fbbc1SLuiz Capitulino                                  mount->dirname);
549c216e5adSMichael Roth                 close(fd);
550c216e5adSMichael Roth                 goto error;
551c216e5adSMichael Roth             }
5529e8aded4SMichael Roth         } else {
553c216e5adSMichael Roth             i++;
554c216e5adSMichael Roth         }
5559e8aded4SMichael Roth         close(fd);
5569e8aded4SMichael Roth     }
557c216e5adSMichael Roth 
558af02203fSPaolo Bonzini     free_fs_mount_list(&mounts);
559c216e5adSMichael Roth     return i;
560c216e5adSMichael Roth 
561c216e5adSMichael Roth error:
562af02203fSPaolo Bonzini     free_fs_mount_list(&mounts);
563c216e5adSMichael Roth     qmp_guest_fsfreeze_thaw(NULL);
564c216e5adSMichael Roth     return 0;
565c216e5adSMichael Roth }
566c216e5adSMichael Roth 
567c216e5adSMichael Roth /*
568c216e5adSMichael Roth  * Walk list of frozen file systems in the guest, and thaw them.
569c216e5adSMichael Roth  */
570c216e5adSMichael Roth int64_t qmp_guest_fsfreeze_thaw(Error **err)
571c216e5adSMichael Roth {
572c216e5adSMichael Roth     int ret;
573af02203fSPaolo Bonzini     FsMountList mounts;
574af02203fSPaolo Bonzini     FsMount *mount;
5759e8aded4SMichael Roth     int fd, i = 0, logged;
576261551d1SLuiz Capitulino     Error *local_err = NULL;
577c216e5adSMichael Roth 
5789e8aded4SMichael Roth     QTAILQ_INIT(&mounts);
579261551d1SLuiz Capitulino     build_fs_mount_list(&mounts, &local_err);
580261551d1SLuiz Capitulino     if (error_is_set(&local_err)) {
581261551d1SLuiz Capitulino         error_propagate(err, local_err);
5829e8aded4SMichael Roth         return 0;
5839e8aded4SMichael Roth     }
5849e8aded4SMichael Roth 
5859e8aded4SMichael Roth     QTAILQ_FOREACH(mount, &mounts, next) {
5869e8aded4SMichael Roth         logged = false;
587c216e5adSMichael Roth         fd = qemu_open(mount->dirname, O_RDONLY);
588c216e5adSMichael Roth         if (fd == -1) {
589c216e5adSMichael Roth             continue;
590c216e5adSMichael Roth         }
5919e8aded4SMichael Roth         /* we have no way of knowing whether a filesystem was actually unfrozen
5929e8aded4SMichael Roth          * as a result of a successful call to FITHAW, only that if an error
5939e8aded4SMichael Roth          * was returned the filesystem was *not* unfrozen by that particular
5949e8aded4SMichael Roth          * call.
5959e8aded4SMichael Roth          *
596a31f0531SJim Meyering          * since multiple preceding FIFREEZEs require multiple calls to FITHAW
5979e8aded4SMichael Roth          * to unfreeze, continuing issuing FITHAW until an error is returned,
5989e8aded4SMichael Roth          * in which case either the filesystem is in an unfreezable state, or,
5999e8aded4SMichael Roth          * more likely, it was thawed previously (and remains so afterward).
6009e8aded4SMichael Roth          *
6019e8aded4SMichael Roth          * also, since the most recent successful call is the one that did
6029e8aded4SMichael Roth          * the actual unfreeze, we can use this to provide an accurate count
6039e8aded4SMichael Roth          * of the number of filesystems unfrozen by guest-fsfreeze-thaw, which
6049e8aded4SMichael Roth          * may * be useful for determining whether a filesystem was unfrozen
6059e8aded4SMichael Roth          * during the freeze/thaw phase by a process other than qemu-ga.
6069e8aded4SMichael Roth          */
6079e8aded4SMichael Roth         do {
608c216e5adSMichael Roth             ret = ioctl(fd, FITHAW);
6099e8aded4SMichael Roth             if (ret == 0 && !logged) {
610c216e5adSMichael Roth                 i++;
6119e8aded4SMichael Roth                 logged = true;
6129e8aded4SMichael Roth             }
6139e8aded4SMichael Roth         } while (ret == 0);
6149e8aded4SMichael Roth         close(fd);
615c216e5adSMichael Roth     }
616c216e5adSMichael Roth 
617f22d85e9SMichael Roth     ga_unset_frozen(ga_state);
618af02203fSPaolo Bonzini     free_fs_mount_list(&mounts);
619ec0f694cSTomoki Sekiyama 
620ec0f694cSTomoki Sekiyama     execute_fsfreeze_hook(FSFREEZE_HOOK_THAW, err);
621ec0f694cSTomoki Sekiyama 
622c216e5adSMichael Roth     return i;
623c216e5adSMichael Roth }
624c216e5adSMichael Roth 
625c216e5adSMichael Roth static void guest_fsfreeze_cleanup(void)
626c216e5adSMichael Roth {
627c216e5adSMichael Roth     Error *err = NULL;
628c216e5adSMichael Roth 
629f22d85e9SMichael Roth     if (ga_is_frozen(ga_state) == GUEST_FSFREEZE_STATUS_FROZEN) {
6306f686749SMarkus Armbruster         qmp_guest_fsfreeze_thaw(&err);
6316f686749SMarkus Armbruster         if (err) {
6326f686749SMarkus Armbruster             slog("failed to clean up frozen filesystems: %s",
6336f686749SMarkus Armbruster                  error_get_pretty(err));
6346f686749SMarkus Armbruster             error_free(err);
635c216e5adSMichael Roth         }
636c216e5adSMichael Roth     }
637c216e5adSMichael Roth }
638e72c3f2eSMichael Roth #endif /* CONFIG_FSFREEZE */
639c216e5adSMichael Roth 
640eab5fd59SPaolo Bonzini #if defined(CONFIG_FSTRIM)
641eab5fd59SPaolo Bonzini /*
642eab5fd59SPaolo Bonzini  * Walk list of mounted file systems in the guest, and trim them.
643eab5fd59SPaolo Bonzini  */
644eab5fd59SPaolo Bonzini void qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **err)
645eab5fd59SPaolo Bonzini {
646eab5fd59SPaolo Bonzini     int ret = 0;
647eab5fd59SPaolo Bonzini     FsMountList mounts;
648eab5fd59SPaolo Bonzini     struct FsMount *mount;
649eab5fd59SPaolo Bonzini     int fd;
650261551d1SLuiz Capitulino     Error *local_err = NULL;
651eab5fd59SPaolo Bonzini     struct fstrim_range r = {
652eab5fd59SPaolo Bonzini         .start = 0,
653eab5fd59SPaolo Bonzini         .len = -1,
654eab5fd59SPaolo Bonzini         .minlen = has_minimum ? minimum : 0,
655eab5fd59SPaolo Bonzini     };
656eab5fd59SPaolo Bonzini 
657eab5fd59SPaolo Bonzini     slog("guest-fstrim called");
658eab5fd59SPaolo Bonzini 
659eab5fd59SPaolo Bonzini     QTAILQ_INIT(&mounts);
660261551d1SLuiz Capitulino     build_fs_mount_list(&mounts, &local_err);
661261551d1SLuiz Capitulino     if (error_is_set(&local_err)) {
662261551d1SLuiz Capitulino         error_propagate(err, local_err);
663eab5fd59SPaolo Bonzini         return;
664eab5fd59SPaolo Bonzini     }
665eab5fd59SPaolo Bonzini 
666eab5fd59SPaolo Bonzini     QTAILQ_FOREACH(mount, &mounts, next) {
667eab5fd59SPaolo Bonzini         fd = qemu_open(mount->dirname, O_RDONLY);
668eab5fd59SPaolo Bonzini         if (fd == -1) {
669071673b0SLuiz Capitulino             error_setg_errno(err, errno, "failed to open %s", mount->dirname);
670eab5fd59SPaolo Bonzini             goto error;
671eab5fd59SPaolo Bonzini         }
672eab5fd59SPaolo Bonzini 
673eab5fd59SPaolo Bonzini         /* We try to cull filesytems we know won't work in advance, but other
674eab5fd59SPaolo Bonzini          * filesytems may not implement fstrim for less obvious reasons.  These
675eab5fd59SPaolo Bonzini          * will report EOPNOTSUPP; we simply ignore these errors.  Any other
676eab5fd59SPaolo Bonzini          * error means an unexpected error, so return it in those cases.  In
677eab5fd59SPaolo Bonzini          * some other cases ENOTTY will be reported (e.g. CD-ROMs).
678eab5fd59SPaolo Bonzini          */
679eab5fd59SPaolo Bonzini         ret = ioctl(fd, FITRIM, &r);
680eab5fd59SPaolo Bonzini         if (ret == -1) {
681eab5fd59SPaolo Bonzini             if (errno != ENOTTY && errno != EOPNOTSUPP) {
682071673b0SLuiz Capitulino                 error_setg_errno(err, errno, "failed to trim %s",
683071673b0SLuiz Capitulino                                  mount->dirname);
684eab5fd59SPaolo Bonzini                 close(fd);
685eab5fd59SPaolo Bonzini                 goto error;
686eab5fd59SPaolo Bonzini             }
687eab5fd59SPaolo Bonzini         }
688eab5fd59SPaolo Bonzini         close(fd);
689eab5fd59SPaolo Bonzini     }
690eab5fd59SPaolo Bonzini 
691eab5fd59SPaolo Bonzini error:
692eab5fd59SPaolo Bonzini     free_fs_mount_list(&mounts);
693eab5fd59SPaolo Bonzini }
694eab5fd59SPaolo Bonzini #endif /* CONFIG_FSTRIM */
695eab5fd59SPaolo Bonzini 
696eab5fd59SPaolo Bonzini 
69711d0f125SLuiz Capitulino #define LINUX_SYS_STATE_FILE "/sys/power/state"
69811d0f125SLuiz Capitulino #define SUSPEND_SUPPORTED 0
69911d0f125SLuiz Capitulino #define SUSPEND_NOT_SUPPORTED 1
70011d0f125SLuiz Capitulino 
70111d0f125SLuiz Capitulino static void bios_supports_mode(const char *pmutils_bin, const char *pmutils_arg,
70211d0f125SLuiz Capitulino                                const char *sysfile_str, Error **err)
70311d0f125SLuiz Capitulino {
7046b26e837SLuiz Capitulino     Error *local_err = NULL;
70511d0f125SLuiz Capitulino     char *pmutils_path;
7066b26e837SLuiz Capitulino     pid_t pid;
707dc8764f0SLuiz Capitulino     int status;
70811d0f125SLuiz Capitulino 
70911d0f125SLuiz Capitulino     pmutils_path = g_find_program_in_path(pmutils_bin);
71011d0f125SLuiz Capitulino 
71111d0f125SLuiz Capitulino     pid = fork();
71211d0f125SLuiz Capitulino     if (!pid) {
713dc8764f0SLuiz Capitulino         char buf[32]; /* hopefully big enough */
714dc8764f0SLuiz Capitulino         ssize_t ret;
715dc8764f0SLuiz Capitulino         int fd;
71611d0f125SLuiz Capitulino 
71711d0f125SLuiz Capitulino         setsid();
71811d0f125SLuiz Capitulino         reopen_fd_to_null(0);
71911d0f125SLuiz Capitulino         reopen_fd_to_null(1);
72011d0f125SLuiz Capitulino         reopen_fd_to_null(2);
72111d0f125SLuiz Capitulino 
72211d0f125SLuiz Capitulino         if (pmutils_path) {
72311d0f125SLuiz Capitulino             execle(pmutils_path, pmutils_bin, pmutils_arg, NULL, environ);
72411d0f125SLuiz Capitulino         }
72511d0f125SLuiz Capitulino 
72611d0f125SLuiz Capitulino         /*
72711d0f125SLuiz Capitulino          * If we get here either pm-utils is not installed or execle() has
72811d0f125SLuiz Capitulino          * failed. Let's try the manual method if the caller wants it.
72911d0f125SLuiz Capitulino          */
73011d0f125SLuiz Capitulino 
73111d0f125SLuiz Capitulino         if (!sysfile_str) {
73211d0f125SLuiz Capitulino             _exit(SUSPEND_NOT_SUPPORTED);
73311d0f125SLuiz Capitulino         }
73411d0f125SLuiz Capitulino 
73511d0f125SLuiz Capitulino         fd = open(LINUX_SYS_STATE_FILE, O_RDONLY);
73611d0f125SLuiz Capitulino         if (fd < 0) {
73711d0f125SLuiz Capitulino             _exit(SUSPEND_NOT_SUPPORTED);
73811d0f125SLuiz Capitulino         }
73911d0f125SLuiz Capitulino 
74011d0f125SLuiz Capitulino         ret = read(fd, buf, sizeof(buf)-1);
74111d0f125SLuiz Capitulino         if (ret <= 0) {
74211d0f125SLuiz Capitulino             _exit(SUSPEND_NOT_SUPPORTED);
74311d0f125SLuiz Capitulino         }
74411d0f125SLuiz Capitulino         buf[ret] = '\0';
74511d0f125SLuiz Capitulino 
74611d0f125SLuiz Capitulino         if (strstr(buf, sysfile_str)) {
74711d0f125SLuiz Capitulino             _exit(SUSPEND_SUPPORTED);
74811d0f125SLuiz Capitulino         }
74911d0f125SLuiz Capitulino 
75011d0f125SLuiz Capitulino         _exit(SUSPEND_NOT_SUPPORTED);
7516b26e837SLuiz Capitulino     } else if (pid < 0) {
7526b26e837SLuiz Capitulino         error_setg_errno(err, errno, "failed to create child process");
7536b26e837SLuiz Capitulino         goto out;
75411d0f125SLuiz Capitulino     }
75511d0f125SLuiz Capitulino 
7566b26e837SLuiz Capitulino     ga_wait_child(pid, &status, &local_err);
7576b26e837SLuiz Capitulino     if (error_is_set(&local_err)) {
7586b26e837SLuiz Capitulino         error_propagate(err, local_err);
7596b26e837SLuiz Capitulino         goto out;
76011d0f125SLuiz Capitulino     }
76111d0f125SLuiz Capitulino 
7626b26e837SLuiz Capitulino     if (!WIFEXITED(status)) {
7636b26e837SLuiz Capitulino         error_setg(err, "child process has terminated abnormally");
7646b26e837SLuiz Capitulino         goto out;
7656b26e837SLuiz Capitulino     }
7666b26e837SLuiz Capitulino 
767dc8764f0SLuiz Capitulino     switch (WEXITSTATUS(status)) {
768dc8764f0SLuiz Capitulino     case SUSPEND_SUPPORTED:
7696b26e837SLuiz Capitulino         goto out;
770dc8764f0SLuiz Capitulino     case SUSPEND_NOT_SUPPORTED:
7716b26e837SLuiz Capitulino         error_setg(err,
7726b26e837SLuiz Capitulino                    "the requested suspend mode is not supported by the guest");
7736b26e837SLuiz Capitulino         goto out;
774dc8764f0SLuiz Capitulino     default:
7756b26e837SLuiz Capitulino         error_setg(err,
7766b26e837SLuiz Capitulino                    "the helper program '%s' returned an unexpected exit status"
7776b26e837SLuiz Capitulino                    " code (%d)", pmutils_path, WEXITSTATUS(status));
7786b26e837SLuiz Capitulino         goto out;
779dc8764f0SLuiz Capitulino     }
78011d0f125SLuiz Capitulino 
7816b26e837SLuiz Capitulino out:
7826b26e837SLuiz Capitulino     g_free(pmutils_path);
78311d0f125SLuiz Capitulino }
78411d0f125SLuiz Capitulino 
78511d0f125SLuiz Capitulino static void guest_suspend(const char *pmutils_bin, const char *sysfile_str,
78611d0f125SLuiz Capitulino                           Error **err)
78711d0f125SLuiz Capitulino {
7887b376087SLuiz Capitulino     Error *local_err = NULL;
78911d0f125SLuiz Capitulino     char *pmutils_path;
7907b376087SLuiz Capitulino     pid_t pid;
791dc8764f0SLuiz Capitulino     int status;
79211d0f125SLuiz Capitulino 
79311d0f125SLuiz Capitulino     pmutils_path = g_find_program_in_path(pmutils_bin);
79411d0f125SLuiz Capitulino 
79511d0f125SLuiz Capitulino     pid = fork();
79611d0f125SLuiz Capitulino     if (pid == 0) {
79711d0f125SLuiz Capitulino         /* child */
79811d0f125SLuiz Capitulino         int fd;
79911d0f125SLuiz Capitulino 
80011d0f125SLuiz Capitulino         setsid();
80111d0f125SLuiz Capitulino         reopen_fd_to_null(0);
80211d0f125SLuiz Capitulino         reopen_fd_to_null(1);
80311d0f125SLuiz Capitulino         reopen_fd_to_null(2);
80411d0f125SLuiz Capitulino 
80511d0f125SLuiz Capitulino         if (pmutils_path) {
80611d0f125SLuiz Capitulino             execle(pmutils_path, pmutils_bin, NULL, environ);
80711d0f125SLuiz Capitulino         }
80811d0f125SLuiz Capitulino 
80911d0f125SLuiz Capitulino         /*
81011d0f125SLuiz Capitulino          * If we get here either pm-utils is not installed or execle() has
81111d0f125SLuiz Capitulino          * failed. Let's try the manual method if the caller wants it.
81211d0f125SLuiz Capitulino          */
81311d0f125SLuiz Capitulino 
81411d0f125SLuiz Capitulino         if (!sysfile_str) {
81511d0f125SLuiz Capitulino             _exit(EXIT_FAILURE);
81611d0f125SLuiz Capitulino         }
81711d0f125SLuiz Capitulino 
81811d0f125SLuiz Capitulino         fd = open(LINUX_SYS_STATE_FILE, O_WRONLY);
81911d0f125SLuiz Capitulino         if (fd < 0) {
82011d0f125SLuiz Capitulino             _exit(EXIT_FAILURE);
82111d0f125SLuiz Capitulino         }
82211d0f125SLuiz Capitulino 
82311d0f125SLuiz Capitulino         if (write(fd, sysfile_str, strlen(sysfile_str)) < 0) {
82411d0f125SLuiz Capitulino             _exit(EXIT_FAILURE);
82511d0f125SLuiz Capitulino         }
82611d0f125SLuiz Capitulino 
82711d0f125SLuiz Capitulino         _exit(EXIT_SUCCESS);
8287b376087SLuiz Capitulino     } else if (pid < 0) {
8297b376087SLuiz Capitulino         error_setg_errno(err, errno, "failed to create child process");
8307b376087SLuiz Capitulino         goto out;
83111d0f125SLuiz Capitulino     }
83211d0f125SLuiz Capitulino 
8337b376087SLuiz Capitulino     ga_wait_child(pid, &status, &local_err);
8347b376087SLuiz Capitulino     if (error_is_set(&local_err)) {
8357b376087SLuiz Capitulino         error_propagate(err, local_err);
8367b376087SLuiz Capitulino         goto out;
8377b376087SLuiz Capitulino     }
8387b376087SLuiz Capitulino 
8397b376087SLuiz Capitulino     if (!WIFEXITED(status)) {
8407b376087SLuiz Capitulino         error_setg(err, "child process has terminated abnormally");
8417b376087SLuiz Capitulino         goto out;
8427b376087SLuiz Capitulino     }
8437b376087SLuiz Capitulino 
8447b376087SLuiz Capitulino     if (WEXITSTATUS(status)) {
8457b376087SLuiz Capitulino         error_setg(err, "child process has failed to suspend");
8467b376087SLuiz Capitulino         goto out;
8477b376087SLuiz Capitulino     }
8487b376087SLuiz Capitulino 
8497b376087SLuiz Capitulino out:
85011d0f125SLuiz Capitulino     g_free(pmutils_path);
85111d0f125SLuiz Capitulino }
85211d0f125SLuiz Capitulino 
85311d0f125SLuiz Capitulino void qmp_guest_suspend_disk(Error **err)
85411d0f125SLuiz Capitulino {
85511d0f125SLuiz Capitulino     bios_supports_mode("pm-is-supported", "--hibernate", "disk", err);
85611d0f125SLuiz Capitulino     if (error_is_set(err)) {
85711d0f125SLuiz Capitulino         return;
85811d0f125SLuiz Capitulino     }
85911d0f125SLuiz Capitulino 
86011d0f125SLuiz Capitulino     guest_suspend("pm-hibernate", "disk", err);
86111d0f125SLuiz Capitulino }
86211d0f125SLuiz Capitulino 
863fbf42210SLuiz Capitulino void qmp_guest_suspend_ram(Error **err)
864fbf42210SLuiz Capitulino {
865fbf42210SLuiz Capitulino     bios_supports_mode("pm-is-supported", "--suspend", "mem", err);
866fbf42210SLuiz Capitulino     if (error_is_set(err)) {
867fbf42210SLuiz Capitulino         return;
868fbf42210SLuiz Capitulino     }
869fbf42210SLuiz Capitulino 
870fbf42210SLuiz Capitulino     guest_suspend("pm-suspend", "mem", err);
871fbf42210SLuiz Capitulino }
872fbf42210SLuiz Capitulino 
87395f4f404SLuiz Capitulino void qmp_guest_suspend_hybrid(Error **err)
87495f4f404SLuiz Capitulino {
87595f4f404SLuiz Capitulino     bios_supports_mode("pm-is-supported", "--suspend-hybrid", NULL, err);
87695f4f404SLuiz Capitulino     if (error_is_set(err)) {
87795f4f404SLuiz Capitulino         return;
87895f4f404SLuiz Capitulino     }
87995f4f404SLuiz Capitulino 
88095f4f404SLuiz Capitulino     guest_suspend("pm-suspend-hybrid", NULL, err);
88195f4f404SLuiz Capitulino }
88295f4f404SLuiz Capitulino 
8833424fc9fSMichal Privoznik static GuestNetworkInterfaceList *
8843424fc9fSMichal Privoznik guest_find_interface(GuestNetworkInterfaceList *head,
8853424fc9fSMichal Privoznik                      const char *name)
8863424fc9fSMichal Privoznik {
8873424fc9fSMichal Privoznik     for (; head; head = head->next) {
8883424fc9fSMichal Privoznik         if (strcmp(head->value->name, name) == 0) {
8893424fc9fSMichal Privoznik             break;
8903424fc9fSMichal Privoznik         }
8913424fc9fSMichal Privoznik     }
8923424fc9fSMichal Privoznik 
8933424fc9fSMichal Privoznik     return head;
8943424fc9fSMichal Privoznik }
8953424fc9fSMichal Privoznik 
8963424fc9fSMichal Privoznik /*
8973424fc9fSMichal Privoznik  * Build information about guest interfaces
8983424fc9fSMichal Privoznik  */
8993424fc9fSMichal Privoznik GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp)
9003424fc9fSMichal Privoznik {
9013424fc9fSMichal Privoznik     GuestNetworkInterfaceList *head = NULL, *cur_item = NULL;
9023424fc9fSMichal Privoznik     struct ifaddrs *ifap, *ifa;
9033424fc9fSMichal Privoznik 
9043424fc9fSMichal Privoznik     if (getifaddrs(&ifap) < 0) {
905878a0ae0SLuiz Capitulino         error_setg_errno(errp, errno, "getifaddrs failed");
9063424fc9fSMichal Privoznik         goto error;
9073424fc9fSMichal Privoznik     }
9083424fc9fSMichal Privoznik 
9093424fc9fSMichal Privoznik     for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
9103424fc9fSMichal Privoznik         GuestNetworkInterfaceList *info;
9113424fc9fSMichal Privoznik         GuestIpAddressList **address_list = NULL, *address_item = NULL;
9123424fc9fSMichal Privoznik         char addr4[INET_ADDRSTRLEN];
9133424fc9fSMichal Privoznik         char addr6[INET6_ADDRSTRLEN];
9143424fc9fSMichal Privoznik         int sock;
9153424fc9fSMichal Privoznik         struct ifreq ifr;
9163424fc9fSMichal Privoznik         unsigned char *mac_addr;
9173424fc9fSMichal Privoznik         void *p;
9183424fc9fSMichal Privoznik 
9193424fc9fSMichal Privoznik         g_debug("Processing %s interface", ifa->ifa_name);
9203424fc9fSMichal Privoznik 
9213424fc9fSMichal Privoznik         info = guest_find_interface(head, ifa->ifa_name);
9223424fc9fSMichal Privoznik 
9233424fc9fSMichal Privoznik         if (!info) {
9243424fc9fSMichal Privoznik             info = g_malloc0(sizeof(*info));
9253424fc9fSMichal Privoznik             info->value = g_malloc0(sizeof(*info->value));
9263424fc9fSMichal Privoznik             info->value->name = g_strdup(ifa->ifa_name);
9273424fc9fSMichal Privoznik 
9283424fc9fSMichal Privoznik             if (!cur_item) {
9293424fc9fSMichal Privoznik                 head = cur_item = info;
9303424fc9fSMichal Privoznik             } else {
9313424fc9fSMichal Privoznik                 cur_item->next = info;
9323424fc9fSMichal Privoznik                 cur_item = info;
9333424fc9fSMichal Privoznik             }
9343424fc9fSMichal Privoznik         }
9353424fc9fSMichal Privoznik 
9363424fc9fSMichal Privoznik         if (!info->value->has_hardware_address &&
9373424fc9fSMichal Privoznik             ifa->ifa_flags & SIOCGIFHWADDR) {
9383424fc9fSMichal Privoznik             /* we haven't obtained HW address yet */
9393424fc9fSMichal Privoznik             sock = socket(PF_INET, SOCK_STREAM, 0);
9403424fc9fSMichal Privoznik             if (sock == -1) {
941878a0ae0SLuiz Capitulino                 error_setg_errno(errp, errno, "failed to create socket");
9423424fc9fSMichal Privoznik                 goto error;
9433424fc9fSMichal Privoznik             }
9443424fc9fSMichal Privoznik 
9453424fc9fSMichal Privoznik             memset(&ifr, 0, sizeof(ifr));
9461ab516edSJim Meyering             pstrcpy(ifr.ifr_name, IF_NAMESIZE, info->value->name);
9473424fc9fSMichal Privoznik             if (ioctl(sock, SIOCGIFHWADDR, &ifr) == -1) {
948878a0ae0SLuiz Capitulino                 error_setg_errno(errp, errno,
949878a0ae0SLuiz Capitulino                                  "failed to get MAC address of %s",
950878a0ae0SLuiz Capitulino                                  ifa->ifa_name);
95110a2158fSMarkus Armbruster                 close(sock);
9523424fc9fSMichal Privoznik                 goto error;
9533424fc9fSMichal Privoznik             }
9543424fc9fSMichal Privoznik 
95510a2158fSMarkus Armbruster             close(sock);
9563424fc9fSMichal Privoznik             mac_addr = (unsigned char *) &ifr.ifr_hwaddr.sa_data;
9573424fc9fSMichal Privoznik 
958e4ada482SStefan Weil             info->value->hardware_address =
959e4ada482SStefan Weil                 g_strdup_printf("%02x:%02x:%02x:%02x:%02x:%02x",
9603424fc9fSMichal Privoznik                                 (int) mac_addr[0], (int) mac_addr[1],
9613424fc9fSMichal Privoznik                                 (int) mac_addr[2], (int) mac_addr[3],
962e4ada482SStefan Weil                                 (int) mac_addr[4], (int) mac_addr[5]);
9633424fc9fSMichal Privoznik 
9643424fc9fSMichal Privoznik             info->value->has_hardware_address = true;
9653424fc9fSMichal Privoznik         }
9663424fc9fSMichal Privoznik 
9673424fc9fSMichal Privoznik         if (ifa->ifa_addr &&
9683424fc9fSMichal Privoznik             ifa->ifa_addr->sa_family == AF_INET) {
9693424fc9fSMichal Privoznik             /* interface with IPv4 address */
9703424fc9fSMichal Privoznik             p = &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
9713424fc9fSMichal Privoznik             if (!inet_ntop(AF_INET, p, addr4, sizeof(addr4))) {
972878a0ae0SLuiz Capitulino                 error_setg_errno(errp, errno, "inet_ntop failed");
9733424fc9fSMichal Privoznik                 goto error;
9743424fc9fSMichal Privoznik             }
9753424fc9fSMichal Privoznik 
97610a2158fSMarkus Armbruster             address_item = g_malloc0(sizeof(*address_item));
97710a2158fSMarkus Armbruster             address_item->value = g_malloc0(sizeof(*address_item->value));
9783424fc9fSMichal Privoznik             address_item->value->ip_address = g_strdup(addr4);
9793424fc9fSMichal Privoznik             address_item->value->ip_address_type = GUEST_IP_ADDRESS_TYPE_IPV4;
9803424fc9fSMichal Privoznik 
9813424fc9fSMichal Privoznik             if (ifa->ifa_netmask) {
9823424fc9fSMichal Privoznik                 /* Count the number of set bits in netmask.
9833424fc9fSMichal Privoznik                  * This is safe as '1' and '0' cannot be shuffled in netmask. */
9843424fc9fSMichal Privoznik                 p = &((struct sockaddr_in *)ifa->ifa_netmask)->sin_addr;
9853424fc9fSMichal Privoznik                 address_item->value->prefix = ctpop32(((uint32_t *) p)[0]);
9863424fc9fSMichal Privoznik             }
9873424fc9fSMichal Privoznik         } else if (ifa->ifa_addr &&
9883424fc9fSMichal Privoznik                    ifa->ifa_addr->sa_family == AF_INET6) {
9893424fc9fSMichal Privoznik             /* interface with IPv6 address */
9903424fc9fSMichal Privoznik             p = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
9913424fc9fSMichal Privoznik             if (!inet_ntop(AF_INET6, p, addr6, sizeof(addr6))) {
992878a0ae0SLuiz Capitulino                 error_setg_errno(errp, errno, "inet_ntop failed");
9933424fc9fSMichal Privoznik                 goto error;
9943424fc9fSMichal Privoznik             }
9953424fc9fSMichal Privoznik 
99610a2158fSMarkus Armbruster             address_item = g_malloc0(sizeof(*address_item));
99710a2158fSMarkus Armbruster             address_item->value = g_malloc0(sizeof(*address_item->value));
9983424fc9fSMichal Privoznik             address_item->value->ip_address = g_strdup(addr6);
9993424fc9fSMichal Privoznik             address_item->value->ip_address_type = GUEST_IP_ADDRESS_TYPE_IPV6;
10003424fc9fSMichal Privoznik 
10013424fc9fSMichal Privoznik             if (ifa->ifa_netmask) {
10023424fc9fSMichal Privoznik                 /* Count the number of set bits in netmask.
10033424fc9fSMichal Privoznik                  * This is safe as '1' and '0' cannot be shuffled in netmask. */
10043424fc9fSMichal Privoznik                 p = &((struct sockaddr_in6 *)ifa->ifa_netmask)->sin6_addr;
10053424fc9fSMichal Privoznik                 address_item->value->prefix =
10063424fc9fSMichal Privoznik                     ctpop32(((uint32_t *) p)[0]) +
10073424fc9fSMichal Privoznik                     ctpop32(((uint32_t *) p)[1]) +
10083424fc9fSMichal Privoznik                     ctpop32(((uint32_t *) p)[2]) +
10093424fc9fSMichal Privoznik                     ctpop32(((uint32_t *) p)[3]);
10103424fc9fSMichal Privoznik             }
10113424fc9fSMichal Privoznik         }
10123424fc9fSMichal Privoznik 
10133424fc9fSMichal Privoznik         if (!address_item) {
10143424fc9fSMichal Privoznik             continue;
10153424fc9fSMichal Privoznik         }
10163424fc9fSMichal Privoznik 
10173424fc9fSMichal Privoznik         address_list = &info->value->ip_addresses;
10183424fc9fSMichal Privoznik 
10193424fc9fSMichal Privoznik         while (*address_list && (*address_list)->next) {
10203424fc9fSMichal Privoznik             address_list = &(*address_list)->next;
10213424fc9fSMichal Privoznik         }
10223424fc9fSMichal Privoznik 
10233424fc9fSMichal Privoznik         if (!*address_list) {
10243424fc9fSMichal Privoznik             *address_list = address_item;
10253424fc9fSMichal Privoznik         } else {
10263424fc9fSMichal Privoznik             (*address_list)->next = address_item;
10273424fc9fSMichal Privoznik         }
10283424fc9fSMichal Privoznik 
10293424fc9fSMichal Privoznik         info->value->has_ip_addresses = true;
10303424fc9fSMichal Privoznik 
10313424fc9fSMichal Privoznik 
10323424fc9fSMichal Privoznik     }
10333424fc9fSMichal Privoznik 
10343424fc9fSMichal Privoznik     freeifaddrs(ifap);
10353424fc9fSMichal Privoznik     return head;
10363424fc9fSMichal Privoznik 
10373424fc9fSMichal Privoznik error:
10383424fc9fSMichal Privoznik     freeifaddrs(ifap);
10393424fc9fSMichal Privoznik     qapi_free_GuestNetworkInterfaceList(head);
10403424fc9fSMichal Privoznik     return NULL;
10413424fc9fSMichal Privoznik }
10423424fc9fSMichal Privoznik 
1043e72c3f2eSMichael Roth #else /* defined(__linux__) */
1044e72c3f2eSMichael Roth 
1045e72c3f2eSMichael Roth void qmp_guest_suspend_disk(Error **err)
1046e72c3f2eSMichael Roth {
1047e72c3f2eSMichael Roth     error_set(err, QERR_UNSUPPORTED);
1048e72c3f2eSMichael Roth }
1049e72c3f2eSMichael Roth 
1050e72c3f2eSMichael Roth void qmp_guest_suspend_ram(Error **err)
1051e72c3f2eSMichael Roth {
1052e72c3f2eSMichael Roth     error_set(err, QERR_UNSUPPORTED);
1053e72c3f2eSMichael Roth }
1054e72c3f2eSMichael Roth 
1055e72c3f2eSMichael Roth void qmp_guest_suspend_hybrid(Error **err)
1056e72c3f2eSMichael Roth {
1057e72c3f2eSMichael Roth     error_set(err, QERR_UNSUPPORTED);
1058e72c3f2eSMichael Roth }
1059e72c3f2eSMichael Roth 
1060e72c3f2eSMichael Roth GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp)
1061e72c3f2eSMichael Roth {
1062e72c3f2eSMichael Roth     error_set(errp, QERR_UNSUPPORTED);
1063e72c3f2eSMichael Roth     return NULL;
1064e72c3f2eSMichael Roth }
1065e72c3f2eSMichael Roth 
1066e72c3f2eSMichael Roth #endif
1067e72c3f2eSMichael Roth 
1068d35d4cb5SMichael Roth #if !defined(CONFIG_FSFREEZE)
1069d35d4cb5SMichael Roth 
1070d35d4cb5SMichael Roth GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **err)
1071d35d4cb5SMichael Roth {
1072d35d4cb5SMichael Roth     error_set(err, QERR_UNSUPPORTED);
1073d35d4cb5SMichael Roth 
1074d35d4cb5SMichael Roth     return 0;
1075d35d4cb5SMichael Roth }
1076d35d4cb5SMichael Roth 
1077d35d4cb5SMichael Roth int64_t qmp_guest_fsfreeze_freeze(Error **err)
1078d35d4cb5SMichael Roth {
1079d35d4cb5SMichael Roth     error_set(err, QERR_UNSUPPORTED);
1080d35d4cb5SMichael Roth 
1081d35d4cb5SMichael Roth     return 0;
1082d35d4cb5SMichael Roth }
1083d35d4cb5SMichael Roth 
1084d35d4cb5SMichael Roth int64_t qmp_guest_fsfreeze_thaw(Error **err)
1085d35d4cb5SMichael Roth {
1086d35d4cb5SMichael Roth     error_set(err, QERR_UNSUPPORTED);
1087d35d4cb5SMichael Roth 
1088d35d4cb5SMichael Roth     return 0;
1089d35d4cb5SMichael Roth }
1090eab5fd59SPaolo Bonzini #endif /* CONFIG_FSFREEZE */
1091d35d4cb5SMichael Roth 
1092eab5fd59SPaolo Bonzini #if !defined(CONFIG_FSTRIM)
1093eab5fd59SPaolo Bonzini void qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **err)
1094eab5fd59SPaolo Bonzini {
1095eab5fd59SPaolo Bonzini     error_set(err, QERR_UNSUPPORTED);
1096eab5fd59SPaolo Bonzini }
1097d35d4cb5SMichael Roth #endif
1098d35d4cb5SMichael Roth 
1099c216e5adSMichael Roth /* register init/cleanup routines for stateful command groups */
1100c216e5adSMichael Roth void ga_command_state_init(GAState *s, GACommandState *cs)
1101c216e5adSMichael Roth {
1102c216e5adSMichael Roth #if defined(CONFIG_FSFREEZE)
1103f22d85e9SMichael Roth     ga_command_state_add(cs, NULL, guest_fsfreeze_cleanup);
1104c216e5adSMichael Roth #endif
1105c216e5adSMichael Roth     ga_command_state_add(cs, guest_file_init, NULL);
1106c216e5adSMichael Roth }
1107