]> git.proxmox.com Git - mirror_qemu.git/blame - qga/commands-posix.c
qemu-ga: add guest-suspend-ram
[mirror_qemu.git] / qga / commands-posix.c
CommitLineData
e3d4d252 1/*
42074a9d 2 * QEMU Guest Agent POSIX-specific command implementations
e3d4d252
MR
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>
4eb36d40
AL
14
15#if defined(__linux__)
e3d4d252 16#include <mntent.h>
7006b9cf 17#include <linux/fs.h>
4eb36d40
AL
18
19#if defined(__linux__) && defined(FIFREEZE)
20#define CONFIG_FSFREEZE
7006b9cf 21#endif
4eb36d40
AL
22#endif
23
e3d4d252
MR
24#include <sys/types.h>
25#include <sys/ioctl.h>
11d0f125 26#include <sys/wait.h>
e3d4d252
MR
27#include "qga/guest-agent-core.h"
28#include "qga-qmp-commands.h"
29#include "qerror.h"
30#include "qemu-queue.h"
31
32static GAState *ga_state;
33
11d0f125
LC
34static void reopen_fd_to_null(int fd)
35{
36 int nullfd;
37
38 nullfd = open("/dev/null", O_RDWR);
39 if (nullfd < 0) {
40 return;
41 }
42
43 dup2(nullfd, fd);
44
45 if (nullfd != fd) {
46 close(nullfd);
47 }
48}
49
e3d4d252
MR
50void qmp_guest_shutdown(bool has_mode, const char *mode, Error **err)
51{
52 int ret;
53 const char *shutdown_flag;
54
55 slog("guest-shutdown called, mode: %s", mode);
56 if (!has_mode || strcmp(mode, "powerdown") == 0) {
57 shutdown_flag = "-P";
58 } else if (strcmp(mode, "halt") == 0) {
59 shutdown_flag = "-H";
60 } else if (strcmp(mode, "reboot") == 0) {
61 shutdown_flag = "-r";
62 } else {
63 error_set(err, QERR_INVALID_PARAMETER_VALUE, "mode",
64 "halt|powerdown|reboot");
65 return;
66 }
67
68 ret = fork();
69 if (ret == 0) {
70 /* child, start the shutdown */
71 setsid();
72 fclose(stdin);
73 fclose(stdout);
74 fclose(stderr);
75
76 ret = execl("/sbin/shutdown", "shutdown", shutdown_flag, "+0",
77 "hypervisor initiated shutdown", (char*)NULL);
78 if (ret) {
79 slog("guest-shutdown failed: %s", strerror(errno));
80 }
81 exit(!!ret);
82 } else if (ret < 0) {
83 error_set(err, QERR_UNDEFINED_ERROR);
84 }
85}
86
87typedef struct GuestFileHandle {
88 uint64_t id;
89 FILE *fh;
90 QTAILQ_ENTRY(GuestFileHandle) next;
91} GuestFileHandle;
92
93static struct {
94 QTAILQ_HEAD(, GuestFileHandle) filehandles;
95} guest_file_state;
96
97static void guest_file_handle_add(FILE *fh)
98{
99 GuestFileHandle *gfh;
100
7267c094 101 gfh = g_malloc0(sizeof(GuestFileHandle));
e3d4d252
MR
102 gfh->id = fileno(fh);
103 gfh->fh = fh;
104 QTAILQ_INSERT_TAIL(&guest_file_state.filehandles, gfh, next);
105}
106
107static GuestFileHandle *guest_file_handle_find(int64_t id)
108{
109 GuestFileHandle *gfh;
110
111 QTAILQ_FOREACH(gfh, &guest_file_state.filehandles, next)
112 {
113 if (gfh->id == id) {
114 return gfh;
115 }
116 }
117
118 return NULL;
119}
120
121int64_t qmp_guest_file_open(const char *path, bool has_mode, const char *mode, Error **err)
122{
123 FILE *fh;
124 int fd;
125 int64_t ret = -1;
126
127 if (!has_mode) {
128 mode = "r";
129 }
130 slog("guest-file-open called, filepath: %s, mode: %s", path, mode);
131 fh = fopen(path, mode);
132 if (!fh) {
133 error_set(err, QERR_OPEN_FILE_FAILED, path);
134 return -1;
135 }
136
137 /* set fd non-blocking to avoid common use cases (like reading from a
138 * named pipe) from hanging the agent
139 */
140 fd = fileno(fh);
141 ret = fcntl(fd, F_GETFL);
142 ret = fcntl(fd, F_SETFL, ret | O_NONBLOCK);
143 if (ret == -1) {
144 error_set(err, QERR_QGA_COMMAND_FAILED, "fcntl() failed");
145 fclose(fh);
146 return -1;
147 }
148
149 guest_file_handle_add(fh);
150 slog("guest-file-open, handle: %d", fd);
151 return fd;
152}
153
154void qmp_guest_file_close(int64_t handle, Error **err)
155{
156 GuestFileHandle *gfh = guest_file_handle_find(handle);
157 int ret;
158
159 slog("guest-file-close called, handle: %ld", handle);
160 if (!gfh) {
161 error_set(err, QERR_FD_NOT_FOUND, "handle");
162 return;
163 }
164
165 ret = fclose(gfh->fh);
166 if (ret == -1) {
167 error_set(err, QERR_QGA_COMMAND_FAILED, "fclose() failed");
168 return;
169 }
170
171 QTAILQ_REMOVE(&guest_file_state.filehandles, gfh, next);
7267c094 172 g_free(gfh);
e3d4d252
MR
173}
174
175struct GuestFileRead *qmp_guest_file_read(int64_t handle, bool has_count,
176 int64_t count, Error **err)
177{
178 GuestFileHandle *gfh = guest_file_handle_find(handle);
179 GuestFileRead *read_data = NULL;
180 guchar *buf;
181 FILE *fh;
182 size_t read_count;
183
184 if (!gfh) {
185 error_set(err, QERR_FD_NOT_FOUND, "handle");
186 return NULL;
187 }
188
189 if (!has_count) {
190 count = QGA_READ_COUNT_DEFAULT;
191 } else if (count < 0) {
192 error_set(err, QERR_INVALID_PARAMETER, "count");
193 return NULL;
194 }
195
196 fh = gfh->fh;
7267c094 197 buf = g_malloc0(count+1);
e3d4d252
MR
198 read_count = fread(buf, 1, count, fh);
199 if (ferror(fh)) {
200 slog("guest-file-read failed, handle: %ld", handle);
201 error_set(err, QERR_QGA_COMMAND_FAILED, "fread() failed");
202 } else {
203 buf[read_count] = 0;
7267c094 204 read_data = g_malloc0(sizeof(GuestFileRead));
e3d4d252
MR
205 read_data->count = read_count;
206 read_data->eof = feof(fh);
207 if (read_count) {
208 read_data->buf_b64 = g_base64_encode(buf, read_count);
209 }
210 }
7267c094 211 g_free(buf);
e3d4d252
MR
212 clearerr(fh);
213
214 return read_data;
215}
216
217GuestFileWrite *qmp_guest_file_write(int64_t handle, const char *buf_b64,
218 bool has_count, int64_t count, Error **err)
219{
220 GuestFileWrite *write_data = NULL;
221 guchar *buf;
222 gsize buf_len;
223 int write_count;
224 GuestFileHandle *gfh = guest_file_handle_find(handle);
225 FILE *fh;
226
227 if (!gfh) {
228 error_set(err, QERR_FD_NOT_FOUND, "handle");
229 return NULL;
230 }
231
232 fh = gfh->fh;
233 buf = g_base64_decode(buf_b64, &buf_len);
234
235 if (!has_count) {
236 count = buf_len;
237 } else if (count < 0 || count > buf_len) {
7267c094 238 g_free(buf);
e3d4d252
MR
239 error_set(err, QERR_INVALID_PARAMETER, "count");
240 return NULL;
241 }
242
243 write_count = fwrite(buf, 1, count, fh);
244 if (ferror(fh)) {
245 slog("guest-file-write failed, handle: %ld", handle);
246 error_set(err, QERR_QGA_COMMAND_FAILED, "fwrite() error");
247 } else {
7267c094 248 write_data = g_malloc0(sizeof(GuestFileWrite));
e3d4d252
MR
249 write_data->count = write_count;
250 write_data->eof = feof(fh);
251 }
7267c094 252 g_free(buf);
e3d4d252
MR
253 clearerr(fh);
254
255 return write_data;
256}
257
258struct GuestFileSeek *qmp_guest_file_seek(int64_t handle, int64_t offset,
259 int64_t whence, Error **err)
260{
261 GuestFileHandle *gfh = guest_file_handle_find(handle);
262 GuestFileSeek *seek_data = NULL;
263 FILE *fh;
264 int ret;
265
266 if (!gfh) {
267 error_set(err, QERR_FD_NOT_FOUND, "handle");
268 return NULL;
269 }
270
271 fh = gfh->fh;
272 ret = fseek(fh, offset, whence);
273 if (ret == -1) {
274 error_set(err, QERR_QGA_COMMAND_FAILED, strerror(errno));
275 } else {
7267c094 276 seek_data = g_malloc0(sizeof(GuestFileRead));
e3d4d252
MR
277 seek_data->position = ftell(fh);
278 seek_data->eof = feof(fh);
279 }
280 clearerr(fh);
281
282 return seek_data;
283}
284
285void qmp_guest_file_flush(int64_t handle, Error **err)
286{
287 GuestFileHandle *gfh = guest_file_handle_find(handle);
288 FILE *fh;
289 int ret;
290
291 if (!gfh) {
292 error_set(err, QERR_FD_NOT_FOUND, "handle");
293 return;
294 }
295
296 fh = gfh->fh;
297 ret = fflush(fh);
298 if (ret == EOF) {
299 error_set(err, QERR_QGA_COMMAND_FAILED, strerror(errno));
300 }
301}
302
303static void guest_file_init(void)
304{
305 QTAILQ_INIT(&guest_file_state.filehandles);
306}
307
7006b9cf
AL
308#if defined(CONFIG_FSFREEZE)
309static void disable_logging(void)
310{
311 ga_disable_logging(ga_state);
312}
313
314static void enable_logging(void)
315{
316 ga_enable_logging(ga_state);
317}
318
e3d4d252
MR
319typedef struct GuestFsfreezeMount {
320 char *dirname;
321 char *devtype;
322 QTAILQ_ENTRY(GuestFsfreezeMount) next;
323} GuestFsfreezeMount;
324
325struct {
326 GuestFsfreezeStatus status;
327 QTAILQ_HEAD(, GuestFsfreezeMount) mount_list;
328} guest_fsfreeze_state;
329
330/*
331 * Walk the mount table and build a list of local file systems
332 */
333static int guest_fsfreeze_build_mount_list(void)
334{
335 struct mntent *ment;
336 GuestFsfreezeMount *mount, *temp;
337 char const *mtab = MOUNTED;
338 FILE *fp;
339
340 QTAILQ_FOREACH_SAFE(mount, &guest_fsfreeze_state.mount_list, next, temp) {
341 QTAILQ_REMOVE(&guest_fsfreeze_state.mount_list, mount, next);
7267c094
AL
342 g_free(mount->dirname);
343 g_free(mount->devtype);
344 g_free(mount);
e3d4d252
MR
345 }
346
347 fp = setmntent(mtab, "r");
348 if (!fp) {
349 g_warning("fsfreeze: unable to read mtab");
350 return -1;
351 }
352
353 while ((ment = getmntent(fp))) {
354 /*
355 * An entry which device name doesn't start with a '/' is
356 * either a dummy file system or a network file system.
357 * Add special handling for smbfs and cifs as is done by
358 * coreutils as well.
359 */
360 if ((ment->mnt_fsname[0] != '/') ||
361 (strcmp(ment->mnt_type, "smbfs") == 0) ||
362 (strcmp(ment->mnt_type, "cifs") == 0)) {
363 continue;
364 }
365
7267c094
AL
366 mount = g_malloc0(sizeof(GuestFsfreezeMount));
367 mount->dirname = g_strdup(ment->mnt_dir);
368 mount->devtype = g_strdup(ment->mnt_type);
e3d4d252
MR
369
370 QTAILQ_INSERT_TAIL(&guest_fsfreeze_state.mount_list, mount, next);
371 }
372
373 endmntent(fp);
374
375 return 0;
376}
377
378/*
379 * Return status of freeze/thaw
380 */
381GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **err)
382{
383 return guest_fsfreeze_state.status;
384}
385
386/*
387 * Walk list of mounted file systems in the guest, and freeze the ones which
388 * are real local file systems.
389 */
390int64_t qmp_guest_fsfreeze_freeze(Error **err)
391{
392 int ret = 0, i = 0;
393 struct GuestFsfreezeMount *mount, *temp;
394 int fd;
395 char err_msg[512];
396
397 slog("guest-fsfreeze called");
398
399 if (guest_fsfreeze_state.status == GUEST_FSFREEZE_STATUS_FROZEN) {
400 return 0;
401 }
402
403 ret = guest_fsfreeze_build_mount_list();
404 if (ret < 0) {
405 return ret;
406 }
407
408 /* cannot risk guest agent blocking itself on a write in this state */
409 disable_logging();
410
411 QTAILQ_FOREACH_SAFE(mount, &guest_fsfreeze_state.mount_list, next, temp) {
412 fd = qemu_open(mount->dirname, O_RDONLY);
413 if (fd == -1) {
414 sprintf(err_msg, "failed to open %s, %s", mount->dirname, strerror(errno));
415 error_set(err, QERR_QGA_COMMAND_FAILED, err_msg);
416 goto error;
417 }
418
419 /* we try to cull filesytems we know won't work in advance, but other
420 * filesytems may not implement fsfreeze for less obvious reasons.
421 * these will report EOPNOTSUPP, so we simply ignore them. when
422 * thawing, these filesystems will return an EINVAL instead, due to
423 * not being in a frozen state. Other filesystem-specific
424 * errors may result in EINVAL, however, so the user should check the
425 * number * of filesystems returned here against those returned by the
426 * thaw operation to determine whether everything completed
427 * successfully
428 */
429 ret = ioctl(fd, FIFREEZE);
430 if (ret < 0 && errno != EOPNOTSUPP) {
431 sprintf(err_msg, "failed to freeze %s, %s", mount->dirname, strerror(errno));
432 error_set(err, QERR_QGA_COMMAND_FAILED, err_msg);
433 close(fd);
434 goto error;
435 }
436 close(fd);
437
438 i++;
439 }
440
441 guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_FROZEN;
442 return i;
443
444error:
445 if (i > 0) {
446 qmp_guest_fsfreeze_thaw(NULL);
447 }
448 return 0;
449}
450
451/*
452 * Walk list of frozen file systems in the guest, and thaw them.
453 */
454int64_t qmp_guest_fsfreeze_thaw(Error **err)
455{
456 int ret;
457 GuestFsfreezeMount *mount, *temp;
458 int fd, i = 0;
459 bool has_error = false;
460
461 QTAILQ_FOREACH_SAFE(mount, &guest_fsfreeze_state.mount_list, next, temp) {
462 fd = qemu_open(mount->dirname, O_RDONLY);
463 if (fd == -1) {
464 has_error = true;
465 continue;
466 }
467 ret = ioctl(fd, FITHAW);
468 if (ret < 0 && errno != EOPNOTSUPP && errno != EINVAL) {
469 has_error = true;
470 close(fd);
471 continue;
472 }
473 close(fd);
474 i++;
475 }
476
477 if (has_error) {
478 guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_ERROR;
479 } else {
480 guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_THAWED;
481 }
482 enable_logging();
483 return i;
484}
485
486static void guest_fsfreeze_init(void)
487{
488 guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_THAWED;
489 QTAILQ_INIT(&guest_fsfreeze_state.mount_list);
490}
491
492static void guest_fsfreeze_cleanup(void)
493{
494 int64_t ret;
495 Error *err = NULL;
496
497 if (guest_fsfreeze_state.status == GUEST_FSFREEZE_STATUS_FROZEN) {
498 ret = qmp_guest_fsfreeze_thaw(&err);
499 if (ret < 0 || err) {
500 slog("failed to clean up frozen filesystems");
501 }
502 }
503}
7006b9cf
AL
504#else
505/*
506 * Return status of freeze/thaw
507 */
508GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **err)
509{
9af99f1d 510 error_set(err, QERR_UNSUPPORTED);
7006b9cf
AL
511
512 return 0;
513}
514
515/*
516 * Walk list of mounted file systems in the guest, and freeze the ones which
517 * are real local file systems.
518 */
519int64_t qmp_guest_fsfreeze_freeze(Error **err)
520{
9af99f1d 521 error_set(err, QERR_UNSUPPORTED);
7006b9cf
AL
522
523 return 0;
524}
525
526/*
527 * Walk list of frozen file systems in the guest, and thaw them.
528 */
529int64_t qmp_guest_fsfreeze_thaw(Error **err)
530{
9af99f1d 531 error_set(err, QERR_UNSUPPORTED);
7006b9cf
AL
532
533 return 0;
534}
535#endif
e3d4d252 536
11d0f125
LC
537#define LINUX_SYS_STATE_FILE "/sys/power/state"
538#define SUSPEND_SUPPORTED 0
539#define SUSPEND_NOT_SUPPORTED 1
540
541/**
542 * This function forks twice and the information about the mode support
543 * status is passed to the qemu-ga process via a pipe.
544 *
545 * This approach allows us to keep the way we reap terminated children
546 * in qemu-ga quite simple.
547 */
548static void bios_supports_mode(const char *pmutils_bin, const char *pmutils_arg,
549 const char *sysfile_str, Error **err)
550{
551 pid_t pid;
552 ssize_t ret;
553 char *pmutils_path;
554 int status, pipefds[2];
555
556 if (pipe(pipefds) < 0) {
557 error_set(err, QERR_UNDEFINED_ERROR);
558 return;
559 }
560
561 pmutils_path = g_find_program_in_path(pmutils_bin);
562
563 pid = fork();
564 if (!pid) {
565 struct sigaction act;
566
567 memset(&act, 0, sizeof(act));
568 act.sa_handler = SIG_DFL;
569 sigaction(SIGCHLD, &act, NULL);
570
571 setsid();
572 close(pipefds[0]);
573 reopen_fd_to_null(0);
574 reopen_fd_to_null(1);
575 reopen_fd_to_null(2);
576
577 pid = fork();
578 if (!pid) {
579 int fd;
580 char buf[32]; /* hopefully big enough */
581
582 if (pmutils_path) {
583 execle(pmutils_path, pmutils_bin, pmutils_arg, NULL, environ);
584 }
585
586 /*
587 * If we get here either pm-utils is not installed or execle() has
588 * failed. Let's try the manual method if the caller wants it.
589 */
590
591 if (!sysfile_str) {
592 _exit(SUSPEND_NOT_SUPPORTED);
593 }
594
595 fd = open(LINUX_SYS_STATE_FILE, O_RDONLY);
596 if (fd < 0) {
597 _exit(SUSPEND_NOT_SUPPORTED);
598 }
599
600 ret = read(fd, buf, sizeof(buf)-1);
601 if (ret <= 0) {
602 _exit(SUSPEND_NOT_SUPPORTED);
603 }
604 buf[ret] = '\0';
605
606 if (strstr(buf, sysfile_str)) {
607 _exit(SUSPEND_SUPPORTED);
608 }
609
610 _exit(SUSPEND_NOT_SUPPORTED);
611 }
612
613 if (pid > 0) {
614 wait(&status);
615 } else {
616 status = SUSPEND_NOT_SUPPORTED;
617 }
618
619 ret = write(pipefds[1], &status, sizeof(status));
620 if (ret != sizeof(status)) {
621 _exit(EXIT_FAILURE);
622 }
623
624 _exit(EXIT_SUCCESS);
625 }
626
627 close(pipefds[1]);
628 g_free(pmutils_path);
629
630 if (pid < 0) {
631 error_set(err, QERR_UNDEFINED_ERROR);
632 goto out;
633 }
634
635 ret = read(pipefds[0], &status, sizeof(status));
636 if (ret == sizeof(status) && WIFEXITED(status) &&
637 WEXITSTATUS(status) == SUSPEND_SUPPORTED) {
638 goto out;
639 }
640
641 error_set(err, QERR_UNSUPPORTED);
642
643out:
644 close(pipefds[0]);
645}
646
647static void guest_suspend(const char *pmutils_bin, const char *sysfile_str,
648 Error **err)
649{
650 pid_t pid;
651 char *pmutils_path;
652
653 pmutils_path = g_find_program_in_path(pmutils_bin);
654
655 pid = fork();
656 if (pid == 0) {
657 /* child */
658 int fd;
659
660 setsid();
661 reopen_fd_to_null(0);
662 reopen_fd_to_null(1);
663 reopen_fd_to_null(2);
664
665 if (pmutils_path) {
666 execle(pmutils_path, pmutils_bin, NULL, environ);
667 }
668
669 /*
670 * If we get here either pm-utils is not installed or execle() has
671 * failed. Let's try the manual method if the caller wants it.
672 */
673
674 if (!sysfile_str) {
675 _exit(EXIT_FAILURE);
676 }
677
678 fd = open(LINUX_SYS_STATE_FILE, O_WRONLY);
679 if (fd < 0) {
680 _exit(EXIT_FAILURE);
681 }
682
683 if (write(fd, sysfile_str, strlen(sysfile_str)) < 0) {
684 _exit(EXIT_FAILURE);
685 }
686
687 _exit(EXIT_SUCCESS);
688 }
689
690 g_free(pmutils_path);
691
692 if (pid < 0) {
693 error_set(err, QERR_UNDEFINED_ERROR);
694 return;
695 }
696}
697
698void qmp_guest_suspend_disk(Error **err)
699{
700 bios_supports_mode("pm-is-supported", "--hibernate", "disk", err);
701 if (error_is_set(err)) {
702 return;
703 }
704
705 guest_suspend("pm-hibernate", "disk", err);
706}
707
fbf42210
LC
708void qmp_guest_suspend_ram(Error **err)
709{
710 bios_supports_mode("pm-is-supported", "--suspend", "mem", err);
711 if (error_is_set(err)) {
712 return;
713 }
714
715 guest_suspend("pm-suspend", "mem", err);
716}
717
e3d4d252
MR
718/* register init/cleanup routines for stateful command groups */
719void ga_command_state_init(GAState *s, GACommandState *cs)
720{
721 ga_state = s;
7006b9cf 722#if defined(CONFIG_FSFREEZE)
e3d4d252 723 ga_command_state_add(cs, guest_fsfreeze_init, guest_fsfreeze_cleanup);
7006b9cf 724#endif
e3d4d252
MR
725 ga_command_state_add(cs, guest_file_init, NULL);
726}