xref: /openbmc/qemu/qga/commands-posix.c (revision c216e5ad)
1*c216e5adSMichael Roth /*
2*c216e5adSMichael Roth  * QEMU Guest Agent POSIX-specific command implementations
3*c216e5adSMichael Roth  *
4*c216e5adSMichael Roth  * Copyright IBM Corp. 2011
5*c216e5adSMichael Roth  *
6*c216e5adSMichael Roth  * Authors:
7*c216e5adSMichael Roth  *  Michael Roth      <mdroth@linux.vnet.ibm.com>
8*c216e5adSMichael Roth  *
9*c216e5adSMichael Roth  * This work is licensed under the terms of the GNU GPL, version 2 or later.
10*c216e5adSMichael Roth  * See the COPYING file in the top-level directory.
11*c216e5adSMichael Roth  */
12*c216e5adSMichael Roth 
13*c216e5adSMichael Roth #include <glib.h>
14*c216e5adSMichael Roth 
15*c216e5adSMichael Roth #if defined(__linux__)
16*c216e5adSMichael Roth #include <mntent.h>
17*c216e5adSMichael Roth #include <linux/fs.h>
18*c216e5adSMichael Roth 
19*c216e5adSMichael Roth #if defined(__linux__) && defined(FIFREEZE)
20*c216e5adSMichael Roth #define CONFIG_FSFREEZE
21*c216e5adSMichael Roth #endif
22*c216e5adSMichael Roth #endif
23*c216e5adSMichael Roth 
24*c216e5adSMichael Roth #include <sys/types.h>
25*c216e5adSMichael Roth #include <sys/ioctl.h>
26*c216e5adSMichael Roth #include "qga/guest-agent-core.h"
27*c216e5adSMichael Roth #include "qga-qmp-commands.h"
28*c216e5adSMichael Roth #include "qerror.h"
29*c216e5adSMichael Roth #include "qemu-queue.h"
30*c216e5adSMichael Roth 
31*c216e5adSMichael Roth static GAState *ga_state;
32*c216e5adSMichael Roth 
33*c216e5adSMichael Roth void qmp_guest_shutdown(bool has_mode, const char *mode, Error **err)
34*c216e5adSMichael Roth {
35*c216e5adSMichael Roth     int ret;
36*c216e5adSMichael Roth     const char *shutdown_flag;
37*c216e5adSMichael Roth 
38*c216e5adSMichael Roth     slog("guest-shutdown called, mode: %s", mode);
39*c216e5adSMichael Roth     if (!has_mode || strcmp(mode, "powerdown") == 0) {
40*c216e5adSMichael Roth         shutdown_flag = "-P";
41*c216e5adSMichael Roth     } else if (strcmp(mode, "halt") == 0) {
42*c216e5adSMichael Roth         shutdown_flag = "-H";
43*c216e5adSMichael Roth     } else if (strcmp(mode, "reboot") == 0) {
44*c216e5adSMichael Roth         shutdown_flag = "-r";
45*c216e5adSMichael Roth     } else {
46*c216e5adSMichael Roth         error_set(err, QERR_INVALID_PARAMETER_VALUE, "mode",
47*c216e5adSMichael Roth                   "halt|powerdown|reboot");
48*c216e5adSMichael Roth         return;
49*c216e5adSMichael Roth     }
50*c216e5adSMichael Roth 
51*c216e5adSMichael Roth     ret = fork();
52*c216e5adSMichael Roth     if (ret == 0) {
53*c216e5adSMichael Roth         /* child, start the shutdown */
54*c216e5adSMichael Roth         setsid();
55*c216e5adSMichael Roth         fclose(stdin);
56*c216e5adSMichael Roth         fclose(stdout);
57*c216e5adSMichael Roth         fclose(stderr);
58*c216e5adSMichael Roth 
59*c216e5adSMichael Roth         ret = execl("/sbin/shutdown", "shutdown", shutdown_flag, "+0",
60*c216e5adSMichael Roth                     "hypervisor initiated shutdown", (char*)NULL);
61*c216e5adSMichael Roth         if (ret) {
62*c216e5adSMichael Roth             slog("guest-shutdown failed: %s", strerror(errno));
63*c216e5adSMichael Roth         }
64*c216e5adSMichael Roth         exit(!!ret);
65*c216e5adSMichael Roth     } else if (ret < 0) {
66*c216e5adSMichael Roth         error_set(err, QERR_UNDEFINED_ERROR);
67*c216e5adSMichael Roth     }
68*c216e5adSMichael Roth }
69*c216e5adSMichael Roth 
70*c216e5adSMichael Roth typedef struct GuestFileHandle {
71*c216e5adSMichael Roth     uint64_t id;
72*c216e5adSMichael Roth     FILE *fh;
73*c216e5adSMichael Roth     QTAILQ_ENTRY(GuestFileHandle) next;
74*c216e5adSMichael Roth } GuestFileHandle;
75*c216e5adSMichael Roth 
76*c216e5adSMichael Roth static struct {
77*c216e5adSMichael Roth     QTAILQ_HEAD(, GuestFileHandle) filehandles;
78*c216e5adSMichael Roth } guest_file_state;
79*c216e5adSMichael Roth 
80*c216e5adSMichael Roth static void guest_file_handle_add(FILE *fh)
81*c216e5adSMichael Roth {
82*c216e5adSMichael Roth     GuestFileHandle *gfh;
83*c216e5adSMichael Roth 
84*c216e5adSMichael Roth     gfh = g_malloc0(sizeof(GuestFileHandle));
85*c216e5adSMichael Roth     gfh->id = fileno(fh);
86*c216e5adSMichael Roth     gfh->fh = fh;
87*c216e5adSMichael Roth     QTAILQ_INSERT_TAIL(&guest_file_state.filehandles, gfh, next);
88*c216e5adSMichael Roth }
89*c216e5adSMichael Roth 
90*c216e5adSMichael Roth static GuestFileHandle *guest_file_handle_find(int64_t id)
91*c216e5adSMichael Roth {
92*c216e5adSMichael Roth     GuestFileHandle *gfh;
93*c216e5adSMichael Roth 
94*c216e5adSMichael Roth     QTAILQ_FOREACH(gfh, &guest_file_state.filehandles, next)
95*c216e5adSMichael Roth     {
96*c216e5adSMichael Roth         if (gfh->id == id) {
97*c216e5adSMichael Roth             return gfh;
98*c216e5adSMichael Roth         }
99*c216e5adSMichael Roth     }
100*c216e5adSMichael Roth 
101*c216e5adSMichael Roth     return NULL;
102*c216e5adSMichael Roth }
103*c216e5adSMichael Roth 
104*c216e5adSMichael Roth int64_t qmp_guest_file_open(const char *path, bool has_mode, const char *mode, Error **err)
105*c216e5adSMichael Roth {
106*c216e5adSMichael Roth     FILE *fh;
107*c216e5adSMichael Roth     int fd;
108*c216e5adSMichael Roth     int64_t ret = -1;
109*c216e5adSMichael Roth 
110*c216e5adSMichael Roth     if (!has_mode) {
111*c216e5adSMichael Roth         mode = "r";
112*c216e5adSMichael Roth     }
113*c216e5adSMichael Roth     slog("guest-file-open called, filepath: %s, mode: %s", path, mode);
114*c216e5adSMichael Roth     fh = fopen(path, mode);
115*c216e5adSMichael Roth     if (!fh) {
116*c216e5adSMichael Roth         error_set(err, QERR_OPEN_FILE_FAILED, path);
117*c216e5adSMichael Roth         return -1;
118*c216e5adSMichael Roth     }
119*c216e5adSMichael Roth 
120*c216e5adSMichael Roth     /* set fd non-blocking to avoid common use cases (like reading from a
121*c216e5adSMichael Roth      * named pipe) from hanging the agent
122*c216e5adSMichael Roth      */
123*c216e5adSMichael Roth     fd = fileno(fh);
124*c216e5adSMichael Roth     ret = fcntl(fd, F_GETFL);
125*c216e5adSMichael Roth     ret = fcntl(fd, F_SETFL, ret | O_NONBLOCK);
126*c216e5adSMichael Roth     if (ret == -1) {
127*c216e5adSMichael Roth         error_set(err, QERR_QGA_COMMAND_FAILED, "fcntl() failed");
128*c216e5adSMichael Roth         fclose(fh);
129*c216e5adSMichael Roth         return -1;
130*c216e5adSMichael Roth     }
131*c216e5adSMichael Roth 
132*c216e5adSMichael Roth     guest_file_handle_add(fh);
133*c216e5adSMichael Roth     slog("guest-file-open, handle: %d", fd);
134*c216e5adSMichael Roth     return fd;
135*c216e5adSMichael Roth }
136*c216e5adSMichael Roth 
137*c216e5adSMichael Roth void qmp_guest_file_close(int64_t handle, Error **err)
138*c216e5adSMichael Roth {
139*c216e5adSMichael Roth     GuestFileHandle *gfh = guest_file_handle_find(handle);
140*c216e5adSMichael Roth     int ret;
141*c216e5adSMichael Roth 
142*c216e5adSMichael Roth     slog("guest-file-close called, handle: %ld", handle);
143*c216e5adSMichael Roth     if (!gfh) {
144*c216e5adSMichael Roth         error_set(err, QERR_FD_NOT_FOUND, "handle");
145*c216e5adSMichael Roth         return;
146*c216e5adSMichael Roth     }
147*c216e5adSMichael Roth 
148*c216e5adSMichael Roth     ret = fclose(gfh->fh);
149*c216e5adSMichael Roth     if (ret == -1) {
150*c216e5adSMichael Roth         error_set(err, QERR_QGA_COMMAND_FAILED, "fclose() failed");
151*c216e5adSMichael Roth         return;
152*c216e5adSMichael Roth     }
153*c216e5adSMichael Roth 
154*c216e5adSMichael Roth     QTAILQ_REMOVE(&guest_file_state.filehandles, gfh, next);
155*c216e5adSMichael Roth     g_free(gfh);
156*c216e5adSMichael Roth }
157*c216e5adSMichael Roth 
158*c216e5adSMichael Roth struct GuestFileRead *qmp_guest_file_read(int64_t handle, bool has_count,
159*c216e5adSMichael Roth                                           int64_t count, Error **err)
160*c216e5adSMichael Roth {
161*c216e5adSMichael Roth     GuestFileHandle *gfh = guest_file_handle_find(handle);
162*c216e5adSMichael Roth     GuestFileRead *read_data = NULL;
163*c216e5adSMichael Roth     guchar *buf;
164*c216e5adSMichael Roth     FILE *fh;
165*c216e5adSMichael Roth     size_t read_count;
166*c216e5adSMichael Roth 
167*c216e5adSMichael Roth     if (!gfh) {
168*c216e5adSMichael Roth         error_set(err, QERR_FD_NOT_FOUND, "handle");
169*c216e5adSMichael Roth         return NULL;
170*c216e5adSMichael Roth     }
171*c216e5adSMichael Roth 
172*c216e5adSMichael Roth     if (!has_count) {
173*c216e5adSMichael Roth         count = QGA_READ_COUNT_DEFAULT;
174*c216e5adSMichael Roth     } else if (count < 0) {
175*c216e5adSMichael Roth         error_set(err, QERR_INVALID_PARAMETER, "count");
176*c216e5adSMichael Roth         return NULL;
177*c216e5adSMichael Roth     }
178*c216e5adSMichael Roth 
179*c216e5adSMichael Roth     fh = gfh->fh;
180*c216e5adSMichael Roth     buf = g_malloc0(count+1);
181*c216e5adSMichael Roth     read_count = fread(buf, 1, count, fh);
182*c216e5adSMichael Roth     if (ferror(fh)) {
183*c216e5adSMichael Roth         slog("guest-file-read failed, handle: %ld", handle);
184*c216e5adSMichael Roth         error_set(err, QERR_QGA_COMMAND_FAILED, "fread() failed");
185*c216e5adSMichael Roth     } else {
186*c216e5adSMichael Roth         buf[read_count] = 0;
187*c216e5adSMichael Roth         read_data = g_malloc0(sizeof(GuestFileRead));
188*c216e5adSMichael Roth         read_data->count = read_count;
189*c216e5adSMichael Roth         read_data->eof = feof(fh);
190*c216e5adSMichael Roth         if (read_count) {
191*c216e5adSMichael Roth             read_data->buf_b64 = g_base64_encode(buf, read_count);
192*c216e5adSMichael Roth         }
193*c216e5adSMichael Roth     }
194*c216e5adSMichael Roth     g_free(buf);
195*c216e5adSMichael Roth     clearerr(fh);
196*c216e5adSMichael Roth 
197*c216e5adSMichael Roth     return read_data;
198*c216e5adSMichael Roth }
199*c216e5adSMichael Roth 
200*c216e5adSMichael Roth GuestFileWrite *qmp_guest_file_write(int64_t handle, const char *buf_b64,
201*c216e5adSMichael Roth                                      bool has_count, int64_t count, Error **err)
202*c216e5adSMichael Roth {
203*c216e5adSMichael Roth     GuestFileWrite *write_data = NULL;
204*c216e5adSMichael Roth     guchar *buf;
205*c216e5adSMichael Roth     gsize buf_len;
206*c216e5adSMichael Roth     int write_count;
207*c216e5adSMichael Roth     GuestFileHandle *gfh = guest_file_handle_find(handle);
208*c216e5adSMichael Roth     FILE *fh;
209*c216e5adSMichael Roth 
210*c216e5adSMichael Roth     if (!gfh) {
211*c216e5adSMichael Roth         error_set(err, QERR_FD_NOT_FOUND, "handle");
212*c216e5adSMichael Roth         return NULL;
213*c216e5adSMichael Roth     }
214*c216e5adSMichael Roth 
215*c216e5adSMichael Roth     fh = gfh->fh;
216*c216e5adSMichael Roth     buf = g_base64_decode(buf_b64, &buf_len);
217*c216e5adSMichael Roth 
218*c216e5adSMichael Roth     if (!has_count) {
219*c216e5adSMichael Roth         count = buf_len;
220*c216e5adSMichael Roth     } else if (count < 0 || count > buf_len) {
221*c216e5adSMichael Roth         g_free(buf);
222*c216e5adSMichael Roth         error_set(err, QERR_INVALID_PARAMETER, "count");
223*c216e5adSMichael Roth         return NULL;
224*c216e5adSMichael Roth     }
225*c216e5adSMichael Roth 
226*c216e5adSMichael Roth     write_count = fwrite(buf, 1, count, fh);
227*c216e5adSMichael Roth     if (ferror(fh)) {
228*c216e5adSMichael Roth         slog("guest-file-write failed, handle: %ld", handle);
229*c216e5adSMichael Roth         error_set(err, QERR_QGA_COMMAND_FAILED, "fwrite() error");
230*c216e5adSMichael Roth     } else {
231*c216e5adSMichael Roth         write_data = g_malloc0(sizeof(GuestFileWrite));
232*c216e5adSMichael Roth         write_data->count = write_count;
233*c216e5adSMichael Roth         write_data->eof = feof(fh);
234*c216e5adSMichael Roth     }
235*c216e5adSMichael Roth     g_free(buf);
236*c216e5adSMichael Roth     clearerr(fh);
237*c216e5adSMichael Roth 
238*c216e5adSMichael Roth     return write_data;
239*c216e5adSMichael Roth }
240*c216e5adSMichael Roth 
241*c216e5adSMichael Roth struct GuestFileSeek *qmp_guest_file_seek(int64_t handle, int64_t offset,
242*c216e5adSMichael Roth                                           int64_t whence, Error **err)
243*c216e5adSMichael Roth {
244*c216e5adSMichael Roth     GuestFileHandle *gfh = guest_file_handle_find(handle);
245*c216e5adSMichael Roth     GuestFileSeek *seek_data = NULL;
246*c216e5adSMichael Roth     FILE *fh;
247*c216e5adSMichael Roth     int ret;
248*c216e5adSMichael Roth 
249*c216e5adSMichael Roth     if (!gfh) {
250*c216e5adSMichael Roth         error_set(err, QERR_FD_NOT_FOUND, "handle");
251*c216e5adSMichael Roth         return NULL;
252*c216e5adSMichael Roth     }
253*c216e5adSMichael Roth 
254*c216e5adSMichael Roth     fh = gfh->fh;
255*c216e5adSMichael Roth     ret = fseek(fh, offset, whence);
256*c216e5adSMichael Roth     if (ret == -1) {
257*c216e5adSMichael Roth         error_set(err, QERR_QGA_COMMAND_FAILED, strerror(errno));
258*c216e5adSMichael Roth     } else {
259*c216e5adSMichael Roth         seek_data = g_malloc0(sizeof(GuestFileRead));
260*c216e5adSMichael Roth         seek_data->position = ftell(fh);
261*c216e5adSMichael Roth         seek_data->eof = feof(fh);
262*c216e5adSMichael Roth     }
263*c216e5adSMichael Roth     clearerr(fh);
264*c216e5adSMichael Roth 
265*c216e5adSMichael Roth     return seek_data;
266*c216e5adSMichael Roth }
267*c216e5adSMichael Roth 
268*c216e5adSMichael Roth void qmp_guest_file_flush(int64_t handle, Error **err)
269*c216e5adSMichael Roth {
270*c216e5adSMichael Roth     GuestFileHandle *gfh = guest_file_handle_find(handle);
271*c216e5adSMichael Roth     FILE *fh;
272*c216e5adSMichael Roth     int ret;
273*c216e5adSMichael Roth 
274*c216e5adSMichael Roth     if (!gfh) {
275*c216e5adSMichael Roth         error_set(err, QERR_FD_NOT_FOUND, "handle");
276*c216e5adSMichael Roth         return;
277*c216e5adSMichael Roth     }
278*c216e5adSMichael Roth 
279*c216e5adSMichael Roth     fh = gfh->fh;
280*c216e5adSMichael Roth     ret = fflush(fh);
281*c216e5adSMichael Roth     if (ret == EOF) {
282*c216e5adSMichael Roth         error_set(err, QERR_QGA_COMMAND_FAILED, strerror(errno));
283*c216e5adSMichael Roth     }
284*c216e5adSMichael Roth }
285*c216e5adSMichael Roth 
286*c216e5adSMichael Roth static void guest_file_init(void)
287*c216e5adSMichael Roth {
288*c216e5adSMichael Roth     QTAILQ_INIT(&guest_file_state.filehandles);
289*c216e5adSMichael Roth }
290*c216e5adSMichael Roth 
291*c216e5adSMichael Roth #if defined(CONFIG_FSFREEZE)
292*c216e5adSMichael Roth static void disable_logging(void)
293*c216e5adSMichael Roth {
294*c216e5adSMichael Roth     ga_disable_logging(ga_state);
295*c216e5adSMichael Roth }
296*c216e5adSMichael Roth 
297*c216e5adSMichael Roth static void enable_logging(void)
298*c216e5adSMichael Roth {
299*c216e5adSMichael Roth     ga_enable_logging(ga_state);
300*c216e5adSMichael Roth }
301*c216e5adSMichael Roth 
302*c216e5adSMichael Roth typedef struct GuestFsfreezeMount {
303*c216e5adSMichael Roth     char *dirname;
304*c216e5adSMichael Roth     char *devtype;
305*c216e5adSMichael Roth     QTAILQ_ENTRY(GuestFsfreezeMount) next;
306*c216e5adSMichael Roth } GuestFsfreezeMount;
307*c216e5adSMichael Roth 
308*c216e5adSMichael Roth struct {
309*c216e5adSMichael Roth     GuestFsfreezeStatus status;
310*c216e5adSMichael Roth     QTAILQ_HEAD(, GuestFsfreezeMount) mount_list;
311*c216e5adSMichael Roth } guest_fsfreeze_state;
312*c216e5adSMichael Roth 
313*c216e5adSMichael Roth /*
314*c216e5adSMichael Roth  * Walk the mount table and build a list of local file systems
315*c216e5adSMichael Roth  */
316*c216e5adSMichael Roth static int guest_fsfreeze_build_mount_list(void)
317*c216e5adSMichael Roth {
318*c216e5adSMichael Roth     struct mntent *ment;
319*c216e5adSMichael Roth     GuestFsfreezeMount *mount, *temp;
320*c216e5adSMichael Roth     char const *mtab = MOUNTED;
321*c216e5adSMichael Roth     FILE *fp;
322*c216e5adSMichael Roth 
323*c216e5adSMichael Roth     QTAILQ_FOREACH_SAFE(mount, &guest_fsfreeze_state.mount_list, next, temp) {
324*c216e5adSMichael Roth         QTAILQ_REMOVE(&guest_fsfreeze_state.mount_list, mount, next);
325*c216e5adSMichael Roth         g_free(mount->dirname);
326*c216e5adSMichael Roth         g_free(mount->devtype);
327*c216e5adSMichael Roth         g_free(mount);
328*c216e5adSMichael Roth     }
329*c216e5adSMichael Roth 
330*c216e5adSMichael Roth     fp = setmntent(mtab, "r");
331*c216e5adSMichael Roth     if (!fp) {
332*c216e5adSMichael Roth         g_warning("fsfreeze: unable to read mtab");
333*c216e5adSMichael Roth         return -1;
334*c216e5adSMichael Roth     }
335*c216e5adSMichael Roth 
336*c216e5adSMichael Roth     while ((ment = getmntent(fp))) {
337*c216e5adSMichael Roth         /*
338*c216e5adSMichael Roth          * An entry which device name doesn't start with a '/' is
339*c216e5adSMichael Roth          * either a dummy file system or a network file system.
340*c216e5adSMichael Roth          * Add special handling for smbfs and cifs as is done by
341*c216e5adSMichael Roth          * coreutils as well.
342*c216e5adSMichael Roth          */
343*c216e5adSMichael Roth         if ((ment->mnt_fsname[0] != '/') ||
344*c216e5adSMichael Roth             (strcmp(ment->mnt_type, "smbfs") == 0) ||
345*c216e5adSMichael Roth             (strcmp(ment->mnt_type, "cifs") == 0)) {
346*c216e5adSMichael Roth             continue;
347*c216e5adSMichael Roth         }
348*c216e5adSMichael Roth 
349*c216e5adSMichael Roth         mount = g_malloc0(sizeof(GuestFsfreezeMount));
350*c216e5adSMichael Roth         mount->dirname = g_strdup(ment->mnt_dir);
351*c216e5adSMichael Roth         mount->devtype = g_strdup(ment->mnt_type);
352*c216e5adSMichael Roth 
353*c216e5adSMichael Roth         QTAILQ_INSERT_TAIL(&guest_fsfreeze_state.mount_list, mount, next);
354*c216e5adSMichael Roth     }
355*c216e5adSMichael Roth 
356*c216e5adSMichael Roth     endmntent(fp);
357*c216e5adSMichael Roth 
358*c216e5adSMichael Roth     return 0;
359*c216e5adSMichael Roth }
360*c216e5adSMichael Roth 
361*c216e5adSMichael Roth /*
362*c216e5adSMichael Roth  * Return status of freeze/thaw
363*c216e5adSMichael Roth  */
364*c216e5adSMichael Roth GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **err)
365*c216e5adSMichael Roth {
366*c216e5adSMichael Roth     return guest_fsfreeze_state.status;
367*c216e5adSMichael Roth }
368*c216e5adSMichael Roth 
369*c216e5adSMichael Roth /*
370*c216e5adSMichael Roth  * Walk list of mounted file systems in the guest, and freeze the ones which
371*c216e5adSMichael Roth  * are real local file systems.
372*c216e5adSMichael Roth  */
373*c216e5adSMichael Roth int64_t qmp_guest_fsfreeze_freeze(Error **err)
374*c216e5adSMichael Roth {
375*c216e5adSMichael Roth     int ret = 0, i = 0;
376*c216e5adSMichael Roth     struct GuestFsfreezeMount *mount, *temp;
377*c216e5adSMichael Roth     int fd;
378*c216e5adSMichael Roth     char err_msg[512];
379*c216e5adSMichael Roth 
380*c216e5adSMichael Roth     slog("guest-fsfreeze called");
381*c216e5adSMichael Roth 
382*c216e5adSMichael Roth     if (guest_fsfreeze_state.status == GUEST_FSFREEZE_STATUS_FROZEN) {
383*c216e5adSMichael Roth         return 0;
384*c216e5adSMichael Roth     }
385*c216e5adSMichael Roth 
386*c216e5adSMichael Roth     ret = guest_fsfreeze_build_mount_list();
387*c216e5adSMichael Roth     if (ret < 0) {
388*c216e5adSMichael Roth         return ret;
389*c216e5adSMichael Roth     }
390*c216e5adSMichael Roth 
391*c216e5adSMichael Roth     /* cannot risk guest agent blocking itself on a write in this state */
392*c216e5adSMichael Roth     disable_logging();
393*c216e5adSMichael Roth 
394*c216e5adSMichael Roth     QTAILQ_FOREACH_SAFE(mount, &guest_fsfreeze_state.mount_list, next, temp) {
395*c216e5adSMichael Roth         fd = qemu_open(mount->dirname, O_RDONLY);
396*c216e5adSMichael Roth         if (fd == -1) {
397*c216e5adSMichael Roth             sprintf(err_msg, "failed to open %s, %s", mount->dirname, strerror(errno));
398*c216e5adSMichael Roth             error_set(err, QERR_QGA_COMMAND_FAILED, err_msg);
399*c216e5adSMichael Roth             goto error;
400*c216e5adSMichael Roth         }
401*c216e5adSMichael Roth 
402*c216e5adSMichael Roth         /* we try to cull filesytems we know won't work in advance, but other
403*c216e5adSMichael Roth          * filesytems may not implement fsfreeze for less obvious reasons.
404*c216e5adSMichael Roth          * these will report EOPNOTSUPP, so we simply ignore them. when
405*c216e5adSMichael Roth          * thawing, these filesystems will return an EINVAL instead, due to
406*c216e5adSMichael Roth          * not being in a frozen state. Other filesystem-specific
407*c216e5adSMichael Roth          * errors may result in EINVAL, however, so the user should check the
408*c216e5adSMichael Roth          * number * of filesystems returned here against those returned by the
409*c216e5adSMichael Roth          * thaw operation to determine whether everything completed
410*c216e5adSMichael Roth          * successfully
411*c216e5adSMichael Roth          */
412*c216e5adSMichael Roth         ret = ioctl(fd, FIFREEZE);
413*c216e5adSMichael Roth         if (ret < 0 && errno != EOPNOTSUPP) {
414*c216e5adSMichael Roth             sprintf(err_msg, "failed to freeze %s, %s", mount->dirname, strerror(errno));
415*c216e5adSMichael Roth             error_set(err, QERR_QGA_COMMAND_FAILED, err_msg);
416*c216e5adSMichael Roth             close(fd);
417*c216e5adSMichael Roth             goto error;
418*c216e5adSMichael Roth         }
419*c216e5adSMichael Roth         close(fd);
420*c216e5adSMichael Roth 
421*c216e5adSMichael Roth         i++;
422*c216e5adSMichael Roth     }
423*c216e5adSMichael Roth 
424*c216e5adSMichael Roth     guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_FROZEN;
425*c216e5adSMichael Roth     return i;
426*c216e5adSMichael Roth 
427*c216e5adSMichael Roth error:
428*c216e5adSMichael Roth     if (i > 0) {
429*c216e5adSMichael Roth         qmp_guest_fsfreeze_thaw(NULL);
430*c216e5adSMichael Roth     }
431*c216e5adSMichael Roth     return 0;
432*c216e5adSMichael Roth }
433*c216e5adSMichael Roth 
434*c216e5adSMichael Roth /*
435*c216e5adSMichael Roth  * Walk list of frozen file systems in the guest, and thaw them.
436*c216e5adSMichael Roth  */
437*c216e5adSMichael Roth int64_t qmp_guest_fsfreeze_thaw(Error **err)
438*c216e5adSMichael Roth {
439*c216e5adSMichael Roth     int ret;
440*c216e5adSMichael Roth     GuestFsfreezeMount *mount, *temp;
441*c216e5adSMichael Roth     int fd, i = 0;
442*c216e5adSMichael Roth     bool has_error = false;
443*c216e5adSMichael Roth 
444*c216e5adSMichael Roth     QTAILQ_FOREACH_SAFE(mount, &guest_fsfreeze_state.mount_list, next, temp) {
445*c216e5adSMichael Roth         fd = qemu_open(mount->dirname, O_RDONLY);
446*c216e5adSMichael Roth         if (fd == -1) {
447*c216e5adSMichael Roth             has_error = true;
448*c216e5adSMichael Roth             continue;
449*c216e5adSMichael Roth         }
450*c216e5adSMichael Roth         ret = ioctl(fd, FITHAW);
451*c216e5adSMichael Roth         if (ret < 0 && errno != EOPNOTSUPP && errno != EINVAL) {
452*c216e5adSMichael Roth             has_error = true;
453*c216e5adSMichael Roth             close(fd);
454*c216e5adSMichael Roth             continue;
455*c216e5adSMichael Roth         }
456*c216e5adSMichael Roth         close(fd);
457*c216e5adSMichael Roth         i++;
458*c216e5adSMichael Roth     }
459*c216e5adSMichael Roth 
460*c216e5adSMichael Roth     if (has_error) {
461*c216e5adSMichael Roth         guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_ERROR;
462*c216e5adSMichael Roth     } else {
463*c216e5adSMichael Roth         guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_THAWED;
464*c216e5adSMichael Roth     }
465*c216e5adSMichael Roth     enable_logging();
466*c216e5adSMichael Roth     return i;
467*c216e5adSMichael Roth }
468*c216e5adSMichael Roth 
469*c216e5adSMichael Roth static void guest_fsfreeze_init(void)
470*c216e5adSMichael Roth {
471*c216e5adSMichael Roth     guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_THAWED;
472*c216e5adSMichael Roth     QTAILQ_INIT(&guest_fsfreeze_state.mount_list);
473*c216e5adSMichael Roth }
474*c216e5adSMichael Roth 
475*c216e5adSMichael Roth static void guest_fsfreeze_cleanup(void)
476*c216e5adSMichael Roth {
477*c216e5adSMichael Roth     int64_t ret;
478*c216e5adSMichael Roth     Error *err = NULL;
479*c216e5adSMichael Roth 
480*c216e5adSMichael Roth     if (guest_fsfreeze_state.status == GUEST_FSFREEZE_STATUS_FROZEN) {
481*c216e5adSMichael Roth         ret = qmp_guest_fsfreeze_thaw(&err);
482*c216e5adSMichael Roth         if (ret < 0 || err) {
483*c216e5adSMichael Roth             slog("failed to clean up frozen filesystems");
484*c216e5adSMichael Roth         }
485*c216e5adSMichael Roth     }
486*c216e5adSMichael Roth }
487*c216e5adSMichael Roth #else
488*c216e5adSMichael Roth /*
489*c216e5adSMichael Roth  * Return status of freeze/thaw
490*c216e5adSMichael Roth  */
491*c216e5adSMichael Roth GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **err)
492*c216e5adSMichael Roth {
493*c216e5adSMichael Roth     error_set(err, QERR_UNSUPPORTED);
494*c216e5adSMichael Roth 
495*c216e5adSMichael Roth     return 0;
496*c216e5adSMichael Roth }
497*c216e5adSMichael Roth 
498*c216e5adSMichael Roth /*
499*c216e5adSMichael Roth  * Walk list of mounted file systems in the guest, and freeze the ones which
500*c216e5adSMichael Roth  * are real local file systems.
501*c216e5adSMichael Roth  */
502*c216e5adSMichael Roth int64_t qmp_guest_fsfreeze_freeze(Error **err)
503*c216e5adSMichael Roth {
504*c216e5adSMichael Roth     error_set(err, QERR_UNSUPPORTED);
505*c216e5adSMichael Roth 
506*c216e5adSMichael Roth     return 0;
507*c216e5adSMichael Roth }
508*c216e5adSMichael Roth 
509*c216e5adSMichael Roth /*
510*c216e5adSMichael Roth  * Walk list of frozen file systems in the guest, and thaw them.
511*c216e5adSMichael Roth  */
512*c216e5adSMichael Roth int64_t qmp_guest_fsfreeze_thaw(Error **err)
513*c216e5adSMichael Roth {
514*c216e5adSMichael Roth     error_set(err, QERR_UNSUPPORTED);
515*c216e5adSMichael Roth 
516*c216e5adSMichael Roth     return 0;
517*c216e5adSMichael Roth }
518*c216e5adSMichael Roth #endif
519*c216e5adSMichael Roth 
520*c216e5adSMichael Roth /* register init/cleanup routines for stateful command groups */
521*c216e5adSMichael Roth void ga_command_state_init(GAState *s, GACommandState *cs)
522*c216e5adSMichael Roth {
523*c216e5adSMichael Roth     ga_state = s;
524*c216e5adSMichael Roth #if defined(CONFIG_FSFREEZE)
525*c216e5adSMichael Roth     ga_command_state_add(cs, guest_fsfreeze_init, guest_fsfreeze_cleanup);
526*c216e5adSMichael Roth #endif
527*c216e5adSMichael Roth     ga_command_state_add(cs, guest_file_init, NULL);
528*c216e5adSMichael Roth }
529