xref: /openbmc/qemu/qga/commands-posix.c (revision 95f4f404)
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>
8c216e5adSMichael Roth  *
9c216e5adSMichael Roth  * This work is licensed under the terms of the GNU GPL, version 2 or later.
10c216e5adSMichael Roth  * See the COPYING file in the top-level directory.
11c216e5adSMichael Roth  */
12c216e5adSMichael Roth 
13c216e5adSMichael Roth #include <glib.h>
14c216e5adSMichael Roth 
15c216e5adSMichael Roth #if defined(__linux__)
16c216e5adSMichael Roth #include <mntent.h>
17c216e5adSMichael Roth #include <linux/fs.h>
18c216e5adSMichael Roth 
19c216e5adSMichael Roth #if defined(__linux__) && defined(FIFREEZE)
20c216e5adSMichael Roth #define CONFIG_FSFREEZE
21c216e5adSMichael Roth #endif
22c216e5adSMichael Roth #endif
23c216e5adSMichael Roth 
24c216e5adSMichael Roth #include <sys/types.h>
25c216e5adSMichael Roth #include <sys/ioctl.h>
2611d0f125SLuiz Capitulino #include <sys/wait.h>
27c216e5adSMichael Roth #include "qga/guest-agent-core.h"
28c216e5adSMichael Roth #include "qga-qmp-commands.h"
29c216e5adSMichael Roth #include "qerror.h"
30c216e5adSMichael Roth #include "qemu-queue.h"
31c216e5adSMichael Roth 
32c216e5adSMichael Roth static GAState *ga_state;
33c216e5adSMichael Roth 
3411d0f125SLuiz Capitulino static void reopen_fd_to_null(int fd)
3511d0f125SLuiz Capitulino {
3611d0f125SLuiz Capitulino     int nullfd;
3711d0f125SLuiz Capitulino 
3811d0f125SLuiz Capitulino     nullfd = open("/dev/null", O_RDWR);
3911d0f125SLuiz Capitulino     if (nullfd < 0) {
4011d0f125SLuiz Capitulino         return;
4111d0f125SLuiz Capitulino     }
4211d0f125SLuiz Capitulino 
4311d0f125SLuiz Capitulino     dup2(nullfd, fd);
4411d0f125SLuiz Capitulino 
4511d0f125SLuiz Capitulino     if (nullfd != fd) {
4611d0f125SLuiz Capitulino         close(nullfd);
4711d0f125SLuiz Capitulino     }
4811d0f125SLuiz Capitulino }
4911d0f125SLuiz Capitulino 
50c216e5adSMichael Roth void qmp_guest_shutdown(bool has_mode, const char *mode, Error **err)
51c216e5adSMichael Roth {
52c216e5adSMichael Roth     int ret;
53c216e5adSMichael Roth     const char *shutdown_flag;
54c216e5adSMichael Roth 
55c216e5adSMichael Roth     slog("guest-shutdown called, mode: %s", mode);
56c216e5adSMichael Roth     if (!has_mode || strcmp(mode, "powerdown") == 0) {
57c216e5adSMichael Roth         shutdown_flag = "-P";
58c216e5adSMichael Roth     } else if (strcmp(mode, "halt") == 0) {
59c216e5adSMichael Roth         shutdown_flag = "-H";
60c216e5adSMichael Roth     } else if (strcmp(mode, "reboot") == 0) {
61c216e5adSMichael Roth         shutdown_flag = "-r";
62c216e5adSMichael Roth     } else {
63c216e5adSMichael Roth         error_set(err, QERR_INVALID_PARAMETER_VALUE, "mode",
64c216e5adSMichael Roth                   "halt|powerdown|reboot");
65c216e5adSMichael Roth         return;
66c216e5adSMichael Roth     }
67c216e5adSMichael Roth 
68c216e5adSMichael Roth     ret = fork();
69c216e5adSMichael Roth     if (ret == 0) {
70c216e5adSMichael Roth         /* child, start the shutdown */
71c216e5adSMichael Roth         setsid();
72c216e5adSMichael Roth         fclose(stdin);
73c216e5adSMichael Roth         fclose(stdout);
74c216e5adSMichael Roth         fclose(stderr);
75c216e5adSMichael Roth 
76c216e5adSMichael Roth         ret = execl("/sbin/shutdown", "shutdown", shutdown_flag, "+0",
77c216e5adSMichael Roth                     "hypervisor initiated shutdown", (char*)NULL);
78c216e5adSMichael Roth         if (ret) {
79c216e5adSMichael Roth             slog("guest-shutdown failed: %s", strerror(errno));
80c216e5adSMichael Roth         }
81c216e5adSMichael Roth         exit(!!ret);
82c216e5adSMichael Roth     } else if (ret < 0) {
83c216e5adSMichael Roth         error_set(err, QERR_UNDEFINED_ERROR);
84c216e5adSMichael Roth     }
85c216e5adSMichael Roth }
86c216e5adSMichael Roth 
87c216e5adSMichael Roth typedef struct GuestFileHandle {
88c216e5adSMichael Roth     uint64_t id;
89c216e5adSMichael Roth     FILE *fh;
90c216e5adSMichael Roth     QTAILQ_ENTRY(GuestFileHandle) next;
91c216e5adSMichael Roth } GuestFileHandle;
92c216e5adSMichael Roth 
93c216e5adSMichael Roth static struct {
94c216e5adSMichael Roth     QTAILQ_HEAD(, GuestFileHandle) filehandles;
95c216e5adSMichael Roth } guest_file_state;
96c216e5adSMichael Roth 
97c216e5adSMichael Roth static void guest_file_handle_add(FILE *fh)
98c216e5adSMichael Roth {
99c216e5adSMichael Roth     GuestFileHandle *gfh;
100c216e5adSMichael Roth 
101c216e5adSMichael Roth     gfh = g_malloc0(sizeof(GuestFileHandle));
102c216e5adSMichael Roth     gfh->id = fileno(fh);
103c216e5adSMichael Roth     gfh->fh = fh;
104c216e5adSMichael Roth     QTAILQ_INSERT_TAIL(&guest_file_state.filehandles, gfh, next);
105c216e5adSMichael Roth }
106c216e5adSMichael Roth 
107c216e5adSMichael Roth static GuestFileHandle *guest_file_handle_find(int64_t id)
108c216e5adSMichael Roth {
109c216e5adSMichael Roth     GuestFileHandle *gfh;
110c216e5adSMichael Roth 
111c216e5adSMichael Roth     QTAILQ_FOREACH(gfh, &guest_file_state.filehandles, next)
112c216e5adSMichael Roth     {
113c216e5adSMichael Roth         if (gfh->id == id) {
114c216e5adSMichael Roth             return gfh;
115c216e5adSMichael Roth         }
116c216e5adSMichael Roth     }
117c216e5adSMichael Roth 
118c216e5adSMichael Roth     return NULL;
119c216e5adSMichael Roth }
120c216e5adSMichael Roth 
121c216e5adSMichael Roth int64_t qmp_guest_file_open(const char *path, bool has_mode, const char *mode, Error **err)
122c216e5adSMichael Roth {
123c216e5adSMichael Roth     FILE *fh;
124c216e5adSMichael Roth     int fd;
125c216e5adSMichael Roth     int64_t ret = -1;
126c216e5adSMichael Roth 
127c216e5adSMichael Roth     if (!has_mode) {
128c216e5adSMichael Roth         mode = "r";
129c216e5adSMichael Roth     }
130c216e5adSMichael Roth     slog("guest-file-open called, filepath: %s, mode: %s", path, mode);
131c216e5adSMichael Roth     fh = fopen(path, mode);
132c216e5adSMichael Roth     if (!fh) {
133c216e5adSMichael Roth         error_set(err, QERR_OPEN_FILE_FAILED, path);
134c216e5adSMichael Roth         return -1;
135c216e5adSMichael Roth     }
136c216e5adSMichael Roth 
137c216e5adSMichael Roth     /* set fd non-blocking to avoid common use cases (like reading from a
138c216e5adSMichael Roth      * named pipe) from hanging the agent
139c216e5adSMichael Roth      */
140c216e5adSMichael Roth     fd = fileno(fh);
141c216e5adSMichael Roth     ret = fcntl(fd, F_GETFL);
142c216e5adSMichael Roth     ret = fcntl(fd, F_SETFL, ret | O_NONBLOCK);
143c216e5adSMichael Roth     if (ret == -1) {
144c216e5adSMichael Roth         error_set(err, QERR_QGA_COMMAND_FAILED, "fcntl() failed");
145c216e5adSMichael Roth         fclose(fh);
146c216e5adSMichael Roth         return -1;
147c216e5adSMichael Roth     }
148c216e5adSMichael Roth 
149c216e5adSMichael Roth     guest_file_handle_add(fh);
150c216e5adSMichael Roth     slog("guest-file-open, handle: %d", fd);
151c216e5adSMichael Roth     return fd;
152c216e5adSMichael Roth }
153c216e5adSMichael Roth 
154c216e5adSMichael Roth void qmp_guest_file_close(int64_t handle, Error **err)
155c216e5adSMichael Roth {
156c216e5adSMichael Roth     GuestFileHandle *gfh = guest_file_handle_find(handle);
157c216e5adSMichael Roth     int ret;
158c216e5adSMichael Roth 
159c216e5adSMichael Roth     slog("guest-file-close called, handle: %ld", handle);
160c216e5adSMichael Roth     if (!gfh) {
161c216e5adSMichael Roth         error_set(err, QERR_FD_NOT_FOUND, "handle");
162c216e5adSMichael Roth         return;
163c216e5adSMichael Roth     }
164c216e5adSMichael Roth 
165c216e5adSMichael Roth     ret = fclose(gfh->fh);
166c216e5adSMichael Roth     if (ret == -1) {
167c216e5adSMichael Roth         error_set(err, QERR_QGA_COMMAND_FAILED, "fclose() failed");
168c216e5adSMichael Roth         return;
169c216e5adSMichael Roth     }
170c216e5adSMichael Roth 
171c216e5adSMichael Roth     QTAILQ_REMOVE(&guest_file_state.filehandles, gfh, next);
172c216e5adSMichael Roth     g_free(gfh);
173c216e5adSMichael Roth }
174c216e5adSMichael Roth 
175c216e5adSMichael Roth struct GuestFileRead *qmp_guest_file_read(int64_t handle, bool has_count,
176c216e5adSMichael Roth                                           int64_t count, Error **err)
177c216e5adSMichael Roth {
178c216e5adSMichael Roth     GuestFileHandle *gfh = guest_file_handle_find(handle);
179c216e5adSMichael Roth     GuestFileRead *read_data = NULL;
180c216e5adSMichael Roth     guchar *buf;
181c216e5adSMichael Roth     FILE *fh;
182c216e5adSMichael Roth     size_t read_count;
183c216e5adSMichael Roth 
184c216e5adSMichael Roth     if (!gfh) {
185c216e5adSMichael Roth         error_set(err, QERR_FD_NOT_FOUND, "handle");
186c216e5adSMichael Roth         return NULL;
187c216e5adSMichael Roth     }
188c216e5adSMichael Roth 
189c216e5adSMichael Roth     if (!has_count) {
190c216e5adSMichael Roth         count = QGA_READ_COUNT_DEFAULT;
191c216e5adSMichael Roth     } else if (count < 0) {
192c216e5adSMichael Roth         error_set(err, QERR_INVALID_PARAMETER, "count");
193c216e5adSMichael Roth         return NULL;
194c216e5adSMichael Roth     }
195c216e5adSMichael Roth 
196c216e5adSMichael Roth     fh = gfh->fh;
197c216e5adSMichael Roth     buf = g_malloc0(count+1);
198c216e5adSMichael Roth     read_count = fread(buf, 1, count, fh);
199c216e5adSMichael Roth     if (ferror(fh)) {
200c216e5adSMichael Roth         slog("guest-file-read failed, handle: %ld", handle);
201c216e5adSMichael Roth         error_set(err, QERR_QGA_COMMAND_FAILED, "fread() failed");
202c216e5adSMichael Roth     } else {
203c216e5adSMichael Roth         buf[read_count] = 0;
204c216e5adSMichael Roth         read_data = g_malloc0(sizeof(GuestFileRead));
205c216e5adSMichael Roth         read_data->count = read_count;
206c216e5adSMichael Roth         read_data->eof = feof(fh);
207c216e5adSMichael Roth         if (read_count) {
208c216e5adSMichael Roth             read_data->buf_b64 = g_base64_encode(buf, read_count);
209c216e5adSMichael Roth         }
210c216e5adSMichael Roth     }
211c216e5adSMichael Roth     g_free(buf);
212c216e5adSMichael Roth     clearerr(fh);
213c216e5adSMichael Roth 
214c216e5adSMichael Roth     return read_data;
215c216e5adSMichael Roth }
216c216e5adSMichael Roth 
217c216e5adSMichael Roth GuestFileWrite *qmp_guest_file_write(int64_t handle, const char *buf_b64,
218c216e5adSMichael Roth                                      bool has_count, int64_t count, Error **err)
219c216e5adSMichael Roth {
220c216e5adSMichael Roth     GuestFileWrite *write_data = NULL;
221c216e5adSMichael Roth     guchar *buf;
222c216e5adSMichael Roth     gsize buf_len;
223c216e5adSMichael Roth     int write_count;
224c216e5adSMichael Roth     GuestFileHandle *gfh = guest_file_handle_find(handle);
225c216e5adSMichael Roth     FILE *fh;
226c216e5adSMichael Roth 
227c216e5adSMichael Roth     if (!gfh) {
228c216e5adSMichael Roth         error_set(err, QERR_FD_NOT_FOUND, "handle");
229c216e5adSMichael Roth         return NULL;
230c216e5adSMichael Roth     }
231c216e5adSMichael Roth 
232c216e5adSMichael Roth     fh = gfh->fh;
233c216e5adSMichael Roth     buf = g_base64_decode(buf_b64, &buf_len);
234c216e5adSMichael Roth 
235c216e5adSMichael Roth     if (!has_count) {
236c216e5adSMichael Roth         count = buf_len;
237c216e5adSMichael Roth     } else if (count < 0 || count > buf_len) {
238c216e5adSMichael Roth         g_free(buf);
239c216e5adSMichael Roth         error_set(err, QERR_INVALID_PARAMETER, "count");
240c216e5adSMichael Roth         return NULL;
241c216e5adSMichael Roth     }
242c216e5adSMichael Roth 
243c216e5adSMichael Roth     write_count = fwrite(buf, 1, count, fh);
244c216e5adSMichael Roth     if (ferror(fh)) {
245c216e5adSMichael Roth         slog("guest-file-write failed, handle: %ld", handle);
246c216e5adSMichael Roth         error_set(err, QERR_QGA_COMMAND_FAILED, "fwrite() error");
247c216e5adSMichael Roth     } else {
248c216e5adSMichael Roth         write_data = g_malloc0(sizeof(GuestFileWrite));
249c216e5adSMichael Roth         write_data->count = write_count;
250c216e5adSMichael Roth         write_data->eof = feof(fh);
251c216e5adSMichael Roth     }
252c216e5adSMichael Roth     g_free(buf);
253c216e5adSMichael Roth     clearerr(fh);
254c216e5adSMichael Roth 
255c216e5adSMichael Roth     return write_data;
256c216e5adSMichael Roth }
257c216e5adSMichael Roth 
258c216e5adSMichael Roth struct GuestFileSeek *qmp_guest_file_seek(int64_t handle, int64_t offset,
259c216e5adSMichael Roth                                           int64_t whence, Error **err)
260c216e5adSMichael Roth {
261c216e5adSMichael Roth     GuestFileHandle *gfh = guest_file_handle_find(handle);
262c216e5adSMichael Roth     GuestFileSeek *seek_data = NULL;
263c216e5adSMichael Roth     FILE *fh;
264c216e5adSMichael Roth     int ret;
265c216e5adSMichael Roth 
266c216e5adSMichael Roth     if (!gfh) {
267c216e5adSMichael Roth         error_set(err, QERR_FD_NOT_FOUND, "handle");
268c216e5adSMichael Roth         return NULL;
269c216e5adSMichael Roth     }
270c216e5adSMichael Roth 
271c216e5adSMichael Roth     fh = gfh->fh;
272c216e5adSMichael Roth     ret = fseek(fh, offset, whence);
273c216e5adSMichael Roth     if (ret == -1) {
274c216e5adSMichael Roth         error_set(err, QERR_QGA_COMMAND_FAILED, strerror(errno));
275c216e5adSMichael Roth     } else {
276c216e5adSMichael Roth         seek_data = g_malloc0(sizeof(GuestFileRead));
277c216e5adSMichael Roth         seek_data->position = ftell(fh);
278c216e5adSMichael Roth         seek_data->eof = feof(fh);
279c216e5adSMichael Roth     }
280c216e5adSMichael Roth     clearerr(fh);
281c216e5adSMichael Roth 
282c216e5adSMichael Roth     return seek_data;
283c216e5adSMichael Roth }
284c216e5adSMichael Roth 
285c216e5adSMichael Roth void qmp_guest_file_flush(int64_t handle, Error **err)
286c216e5adSMichael Roth {
287c216e5adSMichael Roth     GuestFileHandle *gfh = guest_file_handle_find(handle);
288c216e5adSMichael Roth     FILE *fh;
289c216e5adSMichael Roth     int ret;
290c216e5adSMichael Roth 
291c216e5adSMichael Roth     if (!gfh) {
292c216e5adSMichael Roth         error_set(err, QERR_FD_NOT_FOUND, "handle");
293c216e5adSMichael Roth         return;
294c216e5adSMichael Roth     }
295c216e5adSMichael Roth 
296c216e5adSMichael Roth     fh = gfh->fh;
297c216e5adSMichael Roth     ret = fflush(fh);
298c216e5adSMichael Roth     if (ret == EOF) {
299c216e5adSMichael Roth         error_set(err, QERR_QGA_COMMAND_FAILED, strerror(errno));
300c216e5adSMichael Roth     }
301c216e5adSMichael Roth }
302c216e5adSMichael Roth 
303c216e5adSMichael Roth static void guest_file_init(void)
304c216e5adSMichael Roth {
305c216e5adSMichael Roth     QTAILQ_INIT(&guest_file_state.filehandles);
306c216e5adSMichael Roth }
307c216e5adSMichael Roth 
308c216e5adSMichael Roth #if defined(CONFIG_FSFREEZE)
309c216e5adSMichael Roth static void disable_logging(void)
310c216e5adSMichael Roth {
311c216e5adSMichael Roth     ga_disable_logging(ga_state);
312c216e5adSMichael Roth }
313c216e5adSMichael Roth 
314c216e5adSMichael Roth static void enable_logging(void)
315c216e5adSMichael Roth {
316c216e5adSMichael Roth     ga_enable_logging(ga_state);
317c216e5adSMichael Roth }
318c216e5adSMichael Roth 
319c216e5adSMichael Roth typedef struct GuestFsfreezeMount {
320c216e5adSMichael Roth     char *dirname;
321c216e5adSMichael Roth     char *devtype;
322c216e5adSMichael Roth     QTAILQ_ENTRY(GuestFsfreezeMount) next;
323c216e5adSMichael Roth } GuestFsfreezeMount;
324c216e5adSMichael Roth 
325c216e5adSMichael Roth struct {
326c216e5adSMichael Roth     GuestFsfreezeStatus status;
327c216e5adSMichael Roth     QTAILQ_HEAD(, GuestFsfreezeMount) mount_list;
328c216e5adSMichael Roth } guest_fsfreeze_state;
329c216e5adSMichael Roth 
330c216e5adSMichael Roth /*
331c216e5adSMichael Roth  * Walk the mount table and build a list of local file systems
332c216e5adSMichael Roth  */
333c216e5adSMichael Roth static int guest_fsfreeze_build_mount_list(void)
334c216e5adSMichael Roth {
335c216e5adSMichael Roth     struct mntent *ment;
336c216e5adSMichael Roth     GuestFsfreezeMount *mount, *temp;
337c216e5adSMichael Roth     char const *mtab = MOUNTED;
338c216e5adSMichael Roth     FILE *fp;
339c216e5adSMichael Roth 
340c216e5adSMichael Roth     QTAILQ_FOREACH_SAFE(mount, &guest_fsfreeze_state.mount_list, next, temp) {
341c216e5adSMichael Roth         QTAILQ_REMOVE(&guest_fsfreeze_state.mount_list, mount, next);
342c216e5adSMichael Roth         g_free(mount->dirname);
343c216e5adSMichael Roth         g_free(mount->devtype);
344c216e5adSMichael Roth         g_free(mount);
345c216e5adSMichael Roth     }
346c216e5adSMichael Roth 
347c216e5adSMichael Roth     fp = setmntent(mtab, "r");
348c216e5adSMichael Roth     if (!fp) {
349c216e5adSMichael Roth         g_warning("fsfreeze: unable to read mtab");
350c216e5adSMichael Roth         return -1;
351c216e5adSMichael Roth     }
352c216e5adSMichael Roth 
353c216e5adSMichael Roth     while ((ment = getmntent(fp))) {
354c216e5adSMichael Roth         /*
355c216e5adSMichael Roth          * An entry which device name doesn't start with a '/' is
356c216e5adSMichael Roth          * either a dummy file system or a network file system.
357c216e5adSMichael Roth          * Add special handling for smbfs and cifs as is done by
358c216e5adSMichael Roth          * coreutils as well.
359c216e5adSMichael Roth          */
360c216e5adSMichael Roth         if ((ment->mnt_fsname[0] != '/') ||
361c216e5adSMichael Roth             (strcmp(ment->mnt_type, "smbfs") == 0) ||
362c216e5adSMichael Roth             (strcmp(ment->mnt_type, "cifs") == 0)) {
363c216e5adSMichael Roth             continue;
364c216e5adSMichael Roth         }
365c216e5adSMichael Roth 
366c216e5adSMichael Roth         mount = g_malloc0(sizeof(GuestFsfreezeMount));
367c216e5adSMichael Roth         mount->dirname = g_strdup(ment->mnt_dir);
368c216e5adSMichael Roth         mount->devtype = g_strdup(ment->mnt_type);
369c216e5adSMichael Roth 
370c216e5adSMichael Roth         QTAILQ_INSERT_TAIL(&guest_fsfreeze_state.mount_list, mount, next);
371c216e5adSMichael Roth     }
372c216e5adSMichael Roth 
373c216e5adSMichael Roth     endmntent(fp);
374c216e5adSMichael Roth 
375c216e5adSMichael Roth     return 0;
376c216e5adSMichael Roth }
377c216e5adSMichael Roth 
378c216e5adSMichael Roth /*
379c216e5adSMichael Roth  * Return status of freeze/thaw
380c216e5adSMichael Roth  */
381c216e5adSMichael Roth GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **err)
382c216e5adSMichael Roth {
383c216e5adSMichael Roth     return guest_fsfreeze_state.status;
384c216e5adSMichael Roth }
385c216e5adSMichael Roth 
386c216e5adSMichael Roth /*
387c216e5adSMichael Roth  * Walk list of mounted file systems in the guest, and freeze the ones which
388c216e5adSMichael Roth  * are real local file systems.
389c216e5adSMichael Roth  */
390c216e5adSMichael Roth int64_t qmp_guest_fsfreeze_freeze(Error **err)
391c216e5adSMichael Roth {
392c216e5adSMichael Roth     int ret = 0, i = 0;
393c216e5adSMichael Roth     struct GuestFsfreezeMount *mount, *temp;
394c216e5adSMichael Roth     int fd;
395c216e5adSMichael Roth     char err_msg[512];
396c216e5adSMichael Roth 
397c216e5adSMichael Roth     slog("guest-fsfreeze called");
398c216e5adSMichael Roth 
399c216e5adSMichael Roth     if (guest_fsfreeze_state.status == GUEST_FSFREEZE_STATUS_FROZEN) {
400c216e5adSMichael Roth         return 0;
401c216e5adSMichael Roth     }
402c216e5adSMichael Roth 
403c216e5adSMichael Roth     ret = guest_fsfreeze_build_mount_list();
404c216e5adSMichael Roth     if (ret < 0) {
405c216e5adSMichael Roth         return ret;
406c216e5adSMichael Roth     }
407c216e5adSMichael Roth 
408c216e5adSMichael Roth     /* cannot risk guest agent blocking itself on a write in this state */
409c216e5adSMichael Roth     disable_logging();
410c216e5adSMichael Roth 
411c216e5adSMichael Roth     QTAILQ_FOREACH_SAFE(mount, &guest_fsfreeze_state.mount_list, next, temp) {
412c216e5adSMichael Roth         fd = qemu_open(mount->dirname, O_RDONLY);
413c216e5adSMichael Roth         if (fd == -1) {
414c216e5adSMichael Roth             sprintf(err_msg, "failed to open %s, %s", mount->dirname, strerror(errno));
415c216e5adSMichael Roth             error_set(err, QERR_QGA_COMMAND_FAILED, err_msg);
416c216e5adSMichael Roth             goto error;
417c216e5adSMichael Roth         }
418c216e5adSMichael Roth 
419c216e5adSMichael Roth         /* we try to cull filesytems we know won't work in advance, but other
420c216e5adSMichael Roth          * filesytems may not implement fsfreeze for less obvious reasons.
421c216e5adSMichael Roth          * these will report EOPNOTSUPP, so we simply ignore them. when
422c216e5adSMichael Roth          * thawing, these filesystems will return an EINVAL instead, due to
423c216e5adSMichael Roth          * not being in a frozen state. Other filesystem-specific
424c216e5adSMichael Roth          * errors may result in EINVAL, however, so the user should check the
425c216e5adSMichael Roth          * number * of filesystems returned here against those returned by the
426c216e5adSMichael Roth          * thaw operation to determine whether everything completed
427c216e5adSMichael Roth          * successfully
428c216e5adSMichael Roth          */
429c216e5adSMichael Roth         ret = ioctl(fd, FIFREEZE);
430c216e5adSMichael Roth         if (ret < 0 && errno != EOPNOTSUPP) {
431c216e5adSMichael Roth             sprintf(err_msg, "failed to freeze %s, %s", mount->dirname, strerror(errno));
432c216e5adSMichael Roth             error_set(err, QERR_QGA_COMMAND_FAILED, err_msg);
433c216e5adSMichael Roth             close(fd);
434c216e5adSMichael Roth             goto error;
435c216e5adSMichael Roth         }
436c216e5adSMichael Roth         close(fd);
437c216e5adSMichael Roth 
438c216e5adSMichael Roth         i++;
439c216e5adSMichael Roth     }
440c216e5adSMichael Roth 
441c216e5adSMichael Roth     guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_FROZEN;
442c216e5adSMichael Roth     return i;
443c216e5adSMichael Roth 
444c216e5adSMichael Roth error:
445c216e5adSMichael Roth     if (i > 0) {
446c216e5adSMichael Roth         qmp_guest_fsfreeze_thaw(NULL);
447c216e5adSMichael Roth     }
448c216e5adSMichael Roth     return 0;
449c216e5adSMichael Roth }
450c216e5adSMichael Roth 
451c216e5adSMichael Roth /*
452c216e5adSMichael Roth  * Walk list of frozen file systems in the guest, and thaw them.
453c216e5adSMichael Roth  */
454c216e5adSMichael Roth int64_t qmp_guest_fsfreeze_thaw(Error **err)
455c216e5adSMichael Roth {
456c216e5adSMichael Roth     int ret;
457c216e5adSMichael Roth     GuestFsfreezeMount *mount, *temp;
458c216e5adSMichael Roth     int fd, i = 0;
459c216e5adSMichael Roth     bool has_error = false;
460c216e5adSMichael Roth 
461c216e5adSMichael Roth     QTAILQ_FOREACH_SAFE(mount, &guest_fsfreeze_state.mount_list, next, temp) {
462c216e5adSMichael Roth         fd = qemu_open(mount->dirname, O_RDONLY);
463c216e5adSMichael Roth         if (fd == -1) {
464c216e5adSMichael Roth             has_error = true;
465c216e5adSMichael Roth             continue;
466c216e5adSMichael Roth         }
467c216e5adSMichael Roth         ret = ioctl(fd, FITHAW);
468c216e5adSMichael Roth         if (ret < 0 && errno != EOPNOTSUPP && errno != EINVAL) {
469c216e5adSMichael Roth             has_error = true;
470c216e5adSMichael Roth             close(fd);
471c216e5adSMichael Roth             continue;
472c216e5adSMichael Roth         }
473c216e5adSMichael Roth         close(fd);
474c216e5adSMichael Roth         i++;
475c216e5adSMichael Roth     }
476c216e5adSMichael Roth 
477c216e5adSMichael Roth     if (has_error) {
478c216e5adSMichael Roth         guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_ERROR;
479c216e5adSMichael Roth     } else {
480c216e5adSMichael Roth         guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_THAWED;
481c216e5adSMichael Roth     }
482c216e5adSMichael Roth     enable_logging();
483c216e5adSMichael Roth     return i;
484c216e5adSMichael Roth }
485c216e5adSMichael Roth 
486c216e5adSMichael Roth static void guest_fsfreeze_init(void)
487c216e5adSMichael Roth {
488c216e5adSMichael Roth     guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_THAWED;
489c216e5adSMichael Roth     QTAILQ_INIT(&guest_fsfreeze_state.mount_list);
490c216e5adSMichael Roth }
491c216e5adSMichael Roth 
492c216e5adSMichael Roth static void guest_fsfreeze_cleanup(void)
493c216e5adSMichael Roth {
494c216e5adSMichael Roth     int64_t ret;
495c216e5adSMichael Roth     Error *err = NULL;
496c216e5adSMichael Roth 
497c216e5adSMichael Roth     if (guest_fsfreeze_state.status == GUEST_FSFREEZE_STATUS_FROZEN) {
498c216e5adSMichael Roth         ret = qmp_guest_fsfreeze_thaw(&err);
499c216e5adSMichael Roth         if (ret < 0 || err) {
500c216e5adSMichael Roth             slog("failed to clean up frozen filesystems");
501c216e5adSMichael Roth         }
502c216e5adSMichael Roth     }
503c216e5adSMichael Roth }
504c216e5adSMichael Roth #else
505c216e5adSMichael Roth /*
506c216e5adSMichael Roth  * Return status of freeze/thaw
507c216e5adSMichael Roth  */
508c216e5adSMichael Roth GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **err)
509c216e5adSMichael Roth {
510c216e5adSMichael Roth     error_set(err, QERR_UNSUPPORTED);
511c216e5adSMichael Roth 
512c216e5adSMichael Roth     return 0;
513c216e5adSMichael Roth }
514c216e5adSMichael Roth 
515c216e5adSMichael Roth /*
516c216e5adSMichael Roth  * Walk list of mounted file systems in the guest, and freeze the ones which
517c216e5adSMichael Roth  * are real local file systems.
518c216e5adSMichael Roth  */
519c216e5adSMichael Roth int64_t qmp_guest_fsfreeze_freeze(Error **err)
520c216e5adSMichael Roth {
521c216e5adSMichael Roth     error_set(err, QERR_UNSUPPORTED);
522c216e5adSMichael Roth 
523c216e5adSMichael Roth     return 0;
524c216e5adSMichael Roth }
525c216e5adSMichael Roth 
526c216e5adSMichael Roth /*
527c216e5adSMichael Roth  * Walk list of frozen file systems in the guest, and thaw them.
528c216e5adSMichael Roth  */
529c216e5adSMichael Roth int64_t qmp_guest_fsfreeze_thaw(Error **err)
530c216e5adSMichael Roth {
531c216e5adSMichael Roth     error_set(err, QERR_UNSUPPORTED);
532c216e5adSMichael Roth 
533c216e5adSMichael Roth     return 0;
534c216e5adSMichael Roth }
535c216e5adSMichael Roth #endif
536c216e5adSMichael Roth 
53711d0f125SLuiz Capitulino #define LINUX_SYS_STATE_FILE "/sys/power/state"
53811d0f125SLuiz Capitulino #define SUSPEND_SUPPORTED 0
53911d0f125SLuiz Capitulino #define SUSPEND_NOT_SUPPORTED 1
54011d0f125SLuiz Capitulino 
54111d0f125SLuiz Capitulino /**
54211d0f125SLuiz Capitulino  * This function forks twice and the information about the mode support
54311d0f125SLuiz Capitulino  * status is passed to the qemu-ga process via a pipe.
54411d0f125SLuiz Capitulino  *
54511d0f125SLuiz Capitulino  * This approach allows us to keep the way we reap terminated children
54611d0f125SLuiz Capitulino  * in qemu-ga quite simple.
54711d0f125SLuiz Capitulino  */
54811d0f125SLuiz Capitulino static void bios_supports_mode(const char *pmutils_bin, const char *pmutils_arg,
54911d0f125SLuiz Capitulino                                const char *sysfile_str, Error **err)
55011d0f125SLuiz Capitulino {
55111d0f125SLuiz Capitulino     pid_t pid;
55211d0f125SLuiz Capitulino     ssize_t ret;
55311d0f125SLuiz Capitulino     char *pmutils_path;
55411d0f125SLuiz Capitulino     int status, pipefds[2];
55511d0f125SLuiz Capitulino 
55611d0f125SLuiz Capitulino     if (pipe(pipefds) < 0) {
55711d0f125SLuiz Capitulino         error_set(err, QERR_UNDEFINED_ERROR);
55811d0f125SLuiz Capitulino         return;
55911d0f125SLuiz Capitulino     }
56011d0f125SLuiz Capitulino 
56111d0f125SLuiz Capitulino     pmutils_path = g_find_program_in_path(pmutils_bin);
56211d0f125SLuiz Capitulino 
56311d0f125SLuiz Capitulino     pid = fork();
56411d0f125SLuiz Capitulino     if (!pid) {
56511d0f125SLuiz Capitulino         struct sigaction act;
56611d0f125SLuiz Capitulino 
56711d0f125SLuiz Capitulino         memset(&act, 0, sizeof(act));
56811d0f125SLuiz Capitulino         act.sa_handler = SIG_DFL;
56911d0f125SLuiz Capitulino         sigaction(SIGCHLD, &act, NULL);
57011d0f125SLuiz Capitulino 
57111d0f125SLuiz Capitulino         setsid();
57211d0f125SLuiz Capitulino         close(pipefds[0]);
57311d0f125SLuiz Capitulino         reopen_fd_to_null(0);
57411d0f125SLuiz Capitulino         reopen_fd_to_null(1);
57511d0f125SLuiz Capitulino         reopen_fd_to_null(2);
57611d0f125SLuiz Capitulino 
57711d0f125SLuiz Capitulino         pid = fork();
57811d0f125SLuiz Capitulino         if (!pid) {
57911d0f125SLuiz Capitulino             int fd;
58011d0f125SLuiz Capitulino             char buf[32]; /* hopefully big enough */
58111d0f125SLuiz Capitulino 
58211d0f125SLuiz Capitulino             if (pmutils_path) {
58311d0f125SLuiz Capitulino                 execle(pmutils_path, pmutils_bin, pmutils_arg, NULL, environ);
58411d0f125SLuiz Capitulino             }
58511d0f125SLuiz Capitulino 
58611d0f125SLuiz Capitulino             /*
58711d0f125SLuiz Capitulino              * If we get here either pm-utils is not installed or execle() has
58811d0f125SLuiz Capitulino              * failed. Let's try the manual method if the caller wants it.
58911d0f125SLuiz Capitulino              */
59011d0f125SLuiz Capitulino 
59111d0f125SLuiz Capitulino             if (!sysfile_str) {
59211d0f125SLuiz Capitulino                 _exit(SUSPEND_NOT_SUPPORTED);
59311d0f125SLuiz Capitulino             }
59411d0f125SLuiz Capitulino 
59511d0f125SLuiz Capitulino             fd = open(LINUX_SYS_STATE_FILE, O_RDONLY);
59611d0f125SLuiz Capitulino             if (fd < 0) {
59711d0f125SLuiz Capitulino                 _exit(SUSPEND_NOT_SUPPORTED);
59811d0f125SLuiz Capitulino             }
59911d0f125SLuiz Capitulino 
60011d0f125SLuiz Capitulino             ret = read(fd, buf, sizeof(buf)-1);
60111d0f125SLuiz Capitulino             if (ret <= 0) {
60211d0f125SLuiz Capitulino                 _exit(SUSPEND_NOT_SUPPORTED);
60311d0f125SLuiz Capitulino             }
60411d0f125SLuiz Capitulino             buf[ret] = '\0';
60511d0f125SLuiz Capitulino 
60611d0f125SLuiz Capitulino             if (strstr(buf, sysfile_str)) {
60711d0f125SLuiz Capitulino                 _exit(SUSPEND_SUPPORTED);
60811d0f125SLuiz Capitulino             }
60911d0f125SLuiz Capitulino 
61011d0f125SLuiz Capitulino             _exit(SUSPEND_NOT_SUPPORTED);
61111d0f125SLuiz Capitulino         }
61211d0f125SLuiz Capitulino 
61311d0f125SLuiz Capitulino         if (pid > 0) {
61411d0f125SLuiz Capitulino             wait(&status);
61511d0f125SLuiz Capitulino         } else {
61611d0f125SLuiz Capitulino             status = SUSPEND_NOT_SUPPORTED;
61711d0f125SLuiz Capitulino         }
61811d0f125SLuiz Capitulino 
61911d0f125SLuiz Capitulino         ret = write(pipefds[1], &status, sizeof(status));
62011d0f125SLuiz Capitulino         if (ret != sizeof(status)) {
62111d0f125SLuiz Capitulino             _exit(EXIT_FAILURE);
62211d0f125SLuiz Capitulino         }
62311d0f125SLuiz Capitulino 
62411d0f125SLuiz Capitulino         _exit(EXIT_SUCCESS);
62511d0f125SLuiz Capitulino     }
62611d0f125SLuiz Capitulino 
62711d0f125SLuiz Capitulino     close(pipefds[1]);
62811d0f125SLuiz Capitulino     g_free(pmutils_path);
62911d0f125SLuiz Capitulino 
63011d0f125SLuiz Capitulino     if (pid < 0) {
63111d0f125SLuiz Capitulino         error_set(err, QERR_UNDEFINED_ERROR);
63211d0f125SLuiz Capitulino         goto out;
63311d0f125SLuiz Capitulino     }
63411d0f125SLuiz Capitulino 
63511d0f125SLuiz Capitulino     ret = read(pipefds[0], &status, sizeof(status));
63611d0f125SLuiz Capitulino     if (ret == sizeof(status) && WIFEXITED(status) &&
63711d0f125SLuiz Capitulino         WEXITSTATUS(status) == SUSPEND_SUPPORTED) {
63811d0f125SLuiz Capitulino             goto out;
63911d0f125SLuiz Capitulino     }
64011d0f125SLuiz Capitulino 
64111d0f125SLuiz Capitulino     error_set(err, QERR_UNSUPPORTED);
64211d0f125SLuiz Capitulino 
64311d0f125SLuiz Capitulino out:
64411d0f125SLuiz Capitulino     close(pipefds[0]);
64511d0f125SLuiz Capitulino }
64611d0f125SLuiz Capitulino 
64711d0f125SLuiz Capitulino static void guest_suspend(const char *pmutils_bin, const char *sysfile_str,
64811d0f125SLuiz Capitulino                           Error **err)
64911d0f125SLuiz Capitulino {
65011d0f125SLuiz Capitulino     pid_t pid;
65111d0f125SLuiz Capitulino     char *pmutils_path;
65211d0f125SLuiz Capitulino 
65311d0f125SLuiz Capitulino     pmutils_path = g_find_program_in_path(pmutils_bin);
65411d0f125SLuiz Capitulino 
65511d0f125SLuiz Capitulino     pid = fork();
65611d0f125SLuiz Capitulino     if (pid == 0) {
65711d0f125SLuiz Capitulino         /* child */
65811d0f125SLuiz Capitulino         int fd;
65911d0f125SLuiz Capitulino 
66011d0f125SLuiz Capitulino         setsid();
66111d0f125SLuiz Capitulino         reopen_fd_to_null(0);
66211d0f125SLuiz Capitulino         reopen_fd_to_null(1);
66311d0f125SLuiz Capitulino         reopen_fd_to_null(2);
66411d0f125SLuiz Capitulino 
66511d0f125SLuiz Capitulino         if (pmutils_path) {
66611d0f125SLuiz Capitulino             execle(pmutils_path, pmutils_bin, NULL, environ);
66711d0f125SLuiz Capitulino         }
66811d0f125SLuiz Capitulino 
66911d0f125SLuiz Capitulino         /*
67011d0f125SLuiz Capitulino          * If we get here either pm-utils is not installed or execle() has
67111d0f125SLuiz Capitulino          * failed. Let's try the manual method if the caller wants it.
67211d0f125SLuiz Capitulino          */
67311d0f125SLuiz Capitulino 
67411d0f125SLuiz Capitulino         if (!sysfile_str) {
67511d0f125SLuiz Capitulino             _exit(EXIT_FAILURE);
67611d0f125SLuiz Capitulino         }
67711d0f125SLuiz Capitulino 
67811d0f125SLuiz Capitulino         fd = open(LINUX_SYS_STATE_FILE, O_WRONLY);
67911d0f125SLuiz Capitulino         if (fd < 0) {
68011d0f125SLuiz Capitulino             _exit(EXIT_FAILURE);
68111d0f125SLuiz Capitulino         }
68211d0f125SLuiz Capitulino 
68311d0f125SLuiz Capitulino         if (write(fd, sysfile_str, strlen(sysfile_str)) < 0) {
68411d0f125SLuiz Capitulino             _exit(EXIT_FAILURE);
68511d0f125SLuiz Capitulino         }
68611d0f125SLuiz Capitulino 
68711d0f125SLuiz Capitulino         _exit(EXIT_SUCCESS);
68811d0f125SLuiz Capitulino     }
68911d0f125SLuiz Capitulino 
69011d0f125SLuiz Capitulino     g_free(pmutils_path);
69111d0f125SLuiz Capitulino 
69211d0f125SLuiz Capitulino     if (pid < 0) {
69311d0f125SLuiz Capitulino         error_set(err, QERR_UNDEFINED_ERROR);
69411d0f125SLuiz Capitulino         return;
69511d0f125SLuiz Capitulino     }
69611d0f125SLuiz Capitulino }
69711d0f125SLuiz Capitulino 
69811d0f125SLuiz Capitulino void qmp_guest_suspend_disk(Error **err)
69911d0f125SLuiz Capitulino {
70011d0f125SLuiz Capitulino     bios_supports_mode("pm-is-supported", "--hibernate", "disk", err);
70111d0f125SLuiz Capitulino     if (error_is_set(err)) {
70211d0f125SLuiz Capitulino         return;
70311d0f125SLuiz Capitulino     }
70411d0f125SLuiz Capitulino 
70511d0f125SLuiz Capitulino     guest_suspend("pm-hibernate", "disk", err);
70611d0f125SLuiz Capitulino }
70711d0f125SLuiz Capitulino 
708fbf42210SLuiz Capitulino void qmp_guest_suspend_ram(Error **err)
709fbf42210SLuiz Capitulino {
710fbf42210SLuiz Capitulino     bios_supports_mode("pm-is-supported", "--suspend", "mem", err);
711fbf42210SLuiz Capitulino     if (error_is_set(err)) {
712fbf42210SLuiz Capitulino         return;
713fbf42210SLuiz Capitulino     }
714fbf42210SLuiz Capitulino 
715fbf42210SLuiz Capitulino     guest_suspend("pm-suspend", "mem", err);
716fbf42210SLuiz Capitulino }
717fbf42210SLuiz Capitulino 
718*95f4f404SLuiz Capitulino void qmp_guest_suspend_hybrid(Error **err)
719*95f4f404SLuiz Capitulino {
720*95f4f404SLuiz Capitulino     bios_supports_mode("pm-is-supported", "--suspend-hybrid", NULL, err);
721*95f4f404SLuiz Capitulino     if (error_is_set(err)) {
722*95f4f404SLuiz Capitulino         return;
723*95f4f404SLuiz Capitulino     }
724*95f4f404SLuiz Capitulino 
725*95f4f404SLuiz Capitulino     guest_suspend("pm-suspend-hybrid", NULL, err);
726*95f4f404SLuiz Capitulino }
727*95f4f404SLuiz Capitulino 
728c216e5adSMichael Roth /* register init/cleanup routines for stateful command groups */
729c216e5adSMichael Roth void ga_command_state_init(GAState *s, GACommandState *cs)
730c216e5adSMichael Roth {
731c216e5adSMichael Roth     ga_state = s;
732c216e5adSMichael Roth #if defined(CONFIG_FSFREEZE)
733c216e5adSMichael Roth     ga_command_state_add(cs, guest_fsfreeze_init, guest_fsfreeze_cleanup);
734c216e5adSMichael Roth #endif
735c216e5adSMichael Roth     ga_command_state_add(cs, guest_file_init, NULL);
736c216e5adSMichael Roth }
737