2 * QEMU Guest Agent POSIX-specific command implementations
4 * Copyright IBM Corp. 2011
7 * Michael Roth <mdroth@linux.vnet.ibm.com>
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.
15 #if defined(__linux__)
19 #if defined(__linux__) && defined(FIFREEZE)
20 #define CONFIG_FSFREEZE
24 #include <sys/types.h>
25 #include <sys/ioctl.h>
26 #include "qga/guest-agent-core.h"
27 #include "qga-qmp-commands.h"
29 #include "qemu-queue.h"
31 static GAState
*ga_state
;
33 void qmp_guest_shutdown(bool has_mode
, const char *mode
, Error
**err
)
36 const char *shutdown_flag
;
38 slog("guest-shutdown called, mode: %s", mode
);
39 if (!has_mode
|| strcmp(mode
, "powerdown") == 0) {
41 } else if (strcmp(mode
, "halt") == 0) {
43 } else if (strcmp(mode
, "reboot") == 0) {
46 error_set(err
, QERR_INVALID_PARAMETER_VALUE
, "mode",
47 "halt|powerdown|reboot");
53 /* child, start the shutdown */
59 ret
= execl("/sbin/shutdown", "shutdown", shutdown_flag
, "+0",
60 "hypervisor initiated shutdown", (char*)NULL
);
62 slog("guest-shutdown failed: %s", strerror(errno
));
66 error_set(err
, QERR_UNDEFINED_ERROR
);
70 typedef struct GuestFileHandle
{
73 QTAILQ_ENTRY(GuestFileHandle
) next
;
77 QTAILQ_HEAD(, GuestFileHandle
) filehandles
;
80 static void guest_file_handle_add(FILE *fh
)
84 gfh
= g_malloc0(sizeof(GuestFileHandle
));
87 QTAILQ_INSERT_TAIL(&guest_file_state
.filehandles
, gfh
, next
);
90 static GuestFileHandle
*guest_file_handle_find(int64_t id
)
94 QTAILQ_FOREACH(gfh
, &guest_file_state
.filehandles
, next
)
104 int64_t qmp_guest_file_open(const char *path
, bool has_mode
, const char *mode
, Error
**err
)
113 slog("guest-file-open called, filepath: %s, mode: %s", path
, mode
);
114 fh
= fopen(path
, mode
);
116 error_set(err
, QERR_OPEN_FILE_FAILED
, path
);
120 /* set fd non-blocking to avoid common use cases (like reading from a
121 * named pipe) from hanging the agent
124 ret
= fcntl(fd
, F_GETFL
);
125 ret
= fcntl(fd
, F_SETFL
, ret
| O_NONBLOCK
);
127 error_set(err
, QERR_QGA_COMMAND_FAILED
, "fcntl() failed");
132 guest_file_handle_add(fh
);
133 slog("guest-file-open, handle: %d", fd
);
137 void qmp_guest_file_close(int64_t handle
, Error
**err
)
139 GuestFileHandle
*gfh
= guest_file_handle_find(handle
);
142 slog("guest-file-close called, handle: %ld", handle
);
144 error_set(err
, QERR_FD_NOT_FOUND
, "handle");
148 ret
= fclose(gfh
->fh
);
150 error_set(err
, QERR_QGA_COMMAND_FAILED
, "fclose() failed");
154 QTAILQ_REMOVE(&guest_file_state
.filehandles
, gfh
, next
);
158 struct GuestFileRead
*qmp_guest_file_read(int64_t handle
, bool has_count
,
159 int64_t count
, Error
**err
)
161 GuestFileHandle
*gfh
= guest_file_handle_find(handle
);
162 GuestFileRead
*read_data
= NULL
;
168 error_set(err
, QERR_FD_NOT_FOUND
, "handle");
173 count
= QGA_READ_COUNT_DEFAULT
;
174 } else if (count
< 0) {
175 error_set(err
, QERR_INVALID_PARAMETER
, "count");
180 buf
= g_malloc0(count
+1);
181 read_count
= fread(buf
, 1, count
, fh
);
183 slog("guest-file-read failed, handle: %ld", handle
);
184 error_set(err
, QERR_QGA_COMMAND_FAILED
, "fread() failed");
187 read_data
= g_malloc0(sizeof(GuestFileRead
));
188 read_data
->count
= read_count
;
189 read_data
->eof
= feof(fh
);
191 read_data
->buf_b64
= g_base64_encode(buf
, read_count
);
200 GuestFileWrite
*qmp_guest_file_write(int64_t handle
, const char *buf_b64
,
201 bool has_count
, int64_t count
, Error
**err
)
203 GuestFileWrite
*write_data
= NULL
;
207 GuestFileHandle
*gfh
= guest_file_handle_find(handle
);
211 error_set(err
, QERR_FD_NOT_FOUND
, "handle");
216 buf
= g_base64_decode(buf_b64
, &buf_len
);
220 } else if (count
< 0 || count
> buf_len
) {
222 error_set(err
, QERR_INVALID_PARAMETER
, "count");
226 write_count
= fwrite(buf
, 1, count
, fh
);
228 slog("guest-file-write failed, handle: %ld", handle
);
229 error_set(err
, QERR_QGA_COMMAND_FAILED
, "fwrite() error");
231 write_data
= g_malloc0(sizeof(GuestFileWrite
));
232 write_data
->count
= write_count
;
233 write_data
->eof
= feof(fh
);
241 struct GuestFileSeek
*qmp_guest_file_seek(int64_t handle
, int64_t offset
,
242 int64_t whence
, Error
**err
)
244 GuestFileHandle
*gfh
= guest_file_handle_find(handle
);
245 GuestFileSeek
*seek_data
= NULL
;
250 error_set(err
, QERR_FD_NOT_FOUND
, "handle");
255 ret
= fseek(fh
, offset
, whence
);
257 error_set(err
, QERR_QGA_COMMAND_FAILED
, strerror(errno
));
259 seek_data
= g_malloc0(sizeof(GuestFileRead
));
260 seek_data
->position
= ftell(fh
);
261 seek_data
->eof
= feof(fh
);
268 void qmp_guest_file_flush(int64_t handle
, Error
**err
)
270 GuestFileHandle
*gfh
= guest_file_handle_find(handle
);
275 error_set(err
, QERR_FD_NOT_FOUND
, "handle");
282 error_set(err
, QERR_QGA_COMMAND_FAILED
, strerror(errno
));
286 static void guest_file_init(void)
288 QTAILQ_INIT(&guest_file_state
.filehandles
);
291 #if defined(CONFIG_FSFREEZE)
292 static void disable_logging(void)
294 ga_disable_logging(ga_state
);
297 static void enable_logging(void)
299 ga_enable_logging(ga_state
);
302 typedef struct GuestFsfreezeMount
{
305 QTAILQ_ENTRY(GuestFsfreezeMount
) next
;
306 } GuestFsfreezeMount
;
309 GuestFsfreezeStatus status
;
310 QTAILQ_HEAD(, GuestFsfreezeMount
) mount_list
;
311 } guest_fsfreeze_state
;
314 * Walk the mount table and build a list of local file systems
316 static int guest_fsfreeze_build_mount_list(void)
319 GuestFsfreezeMount
*mount
, *temp
;
320 char const *mtab
= MOUNTED
;
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
);
330 fp
= setmntent(mtab
, "r");
332 g_warning("fsfreeze: unable to read mtab");
336 while ((ment
= getmntent(fp
))) {
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
343 if ((ment
->mnt_fsname
[0] != '/') ||
344 (strcmp(ment
->mnt_type
, "smbfs") == 0) ||
345 (strcmp(ment
->mnt_type
, "cifs") == 0)) {
349 mount
= g_malloc0(sizeof(GuestFsfreezeMount
));
350 mount
->dirname
= g_strdup(ment
->mnt_dir
);
351 mount
->devtype
= g_strdup(ment
->mnt_type
);
353 QTAILQ_INSERT_TAIL(&guest_fsfreeze_state
.mount_list
, mount
, next
);
362 * Return status of freeze/thaw
364 GuestFsfreezeStatus
qmp_guest_fsfreeze_status(Error
**err
)
366 return guest_fsfreeze_state
.status
;
370 * Walk list of mounted file systems in the guest, and freeze the ones which
371 * are real local file systems.
373 int64_t qmp_guest_fsfreeze_freeze(Error
**err
)
376 struct GuestFsfreezeMount
*mount
, *temp
;
380 slog("guest-fsfreeze called");
382 if (guest_fsfreeze_state
.status
== GUEST_FSFREEZE_STATUS_FROZEN
) {
386 ret
= guest_fsfreeze_build_mount_list();
391 /* cannot risk guest agent blocking itself on a write in this state */
394 QTAILQ_FOREACH_SAFE(mount
, &guest_fsfreeze_state
.mount_list
, next
, temp
) {
395 fd
= qemu_open(mount
->dirname
, O_RDONLY
);
397 sprintf(err_msg
, "failed to open %s, %s", mount
->dirname
, strerror(errno
));
398 error_set(err
, QERR_QGA_COMMAND_FAILED
, err_msg
);
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
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
);
424 guest_fsfreeze_state
.status
= GUEST_FSFREEZE_STATUS_FROZEN
;
429 qmp_guest_fsfreeze_thaw(NULL
);
435 * Walk list of frozen file systems in the guest, and thaw them.
437 int64_t qmp_guest_fsfreeze_thaw(Error
**err
)
440 GuestFsfreezeMount
*mount
, *temp
;
442 bool has_error
= false;
444 QTAILQ_FOREACH_SAFE(mount
, &guest_fsfreeze_state
.mount_list
, next
, temp
) {
445 fd
= qemu_open(mount
->dirname
, O_RDONLY
);
450 ret
= ioctl(fd
, FITHAW
);
451 if (ret
< 0 && errno
!= EOPNOTSUPP
&& errno
!= EINVAL
) {
461 guest_fsfreeze_state
.status
= GUEST_FSFREEZE_STATUS_ERROR
;
463 guest_fsfreeze_state
.status
= GUEST_FSFREEZE_STATUS_THAWED
;
469 static void guest_fsfreeze_init(void)
471 guest_fsfreeze_state
.status
= GUEST_FSFREEZE_STATUS_THAWED
;
472 QTAILQ_INIT(&guest_fsfreeze_state
.mount_list
);
475 static void guest_fsfreeze_cleanup(void)
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");
489 * Return status of freeze/thaw
491 GuestFsfreezeStatus
qmp_guest_fsfreeze_status(Error
**err
)
493 error_set(err
, QERR_UNSUPPORTED
);
499 * Walk list of mounted file systems in the guest, and freeze the ones which
500 * are real local file systems.
502 int64_t qmp_guest_fsfreeze_freeze(Error
**err
)
504 error_set(err
, QERR_UNSUPPORTED
);
510 * Walk list of frozen file systems in the guest, and thaw them.
512 int64_t qmp_guest_fsfreeze_thaw(Error
**err
)
514 error_set(err
, QERR_UNSUPPORTED
);
520 /* register init/cleanup routines for stateful command groups */
521 void ga_command_state_init(GAState
*s
, GACommandState
*cs
)
524 #if defined(CONFIG_FSFREEZE)
525 ga_command_state_add(cs
, guest_fsfreeze_init
, guest_fsfreeze_cleanup
);
527 ga_command_state_add(cs
, guest_file_init
, NULL
);