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