]>
Commit | Line | Data |
---|---|---|
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> | |
3424fc9f | 8 | * Michal Privoznik <mprivozn@redhat.com> |
e3d4d252 MR |
9 | * |
10 | * This work is licensed under the terms of the GNU GPL, version 2 or later. | |
11 | * See the COPYING file in the top-level directory. | |
12 | */ | |
13 | ||
14 | #include <glib.h> | |
e72c3f2e MR |
15 | #include <sys/types.h> |
16 | #include <sys/ioctl.h> | |
2c02cbf6 | 17 | #include <sys/wait.h> |
e72c3f2e MR |
18 | #include "qga/guest-agent-core.h" |
19 | #include "qga-qmp-commands.h" | |
7b1b5d19 | 20 | #include "qapi/qmp/qerror.h" |
1de7afc9 PB |
21 | #include "qemu/queue.h" |
22 | #include "qemu/host-utils.h" | |
4eb36d40 | 23 | |
2c02cbf6 | 24 | #ifndef CONFIG_HAS_ENVIRON |
eecae147 AF |
25 | #ifdef __APPLE__ |
26 | #include <crt_externs.h> | |
27 | #define environ (*_NSGetEnviron()) | |
28 | #else | |
2c02cbf6 LC |
29 | extern char **environ; |
30 | #endif | |
eecae147 | 31 | #endif |
2c02cbf6 | 32 | |
4eb36d40 | 33 | #if defined(__linux__) |
e3d4d252 | 34 | #include <mntent.h> |
7006b9cf | 35 | #include <linux/fs.h> |
3424fc9f MP |
36 | #include <ifaddrs.h> |
37 | #include <arpa/inet.h> | |
38 | #include <sys/socket.h> | |
39 | #include <net/if.h> | |
e3d4d252 | 40 | |
eab5fd59 | 41 | #ifdef FIFREEZE |
e72c3f2e MR |
42 | #define CONFIG_FSFREEZE |
43 | #endif | |
eab5fd59 PB |
44 | #ifdef FITRIM |
45 | #define CONFIG_FSTRIM | |
46 | #endif | |
e72c3f2e MR |
47 | #endif |
48 | ||
d220a6df LC |
49 | static void ga_wait_child(pid_t pid, int *status, Error **err) |
50 | { | |
51 | pid_t rpid; | |
52 | ||
53 | *status = 0; | |
54 | ||
55 | do { | |
56 | rpid = waitpid(pid, status, 0); | |
57 | } while (rpid == -1 && errno == EINTR); | |
58 | ||
59 | if (rpid == -1) { | |
60 | error_setg_errno(err, errno, "failed to wait for child (pid: %d)", pid); | |
61 | return; | |
62 | } | |
63 | ||
64 | g_assert(rpid == pid); | |
65 | } | |
66 | ||
e3d4d252 MR |
67 | void qmp_guest_shutdown(bool has_mode, const char *mode, Error **err) |
68 | { | |
e3d4d252 | 69 | const char *shutdown_flag; |
d220a6df LC |
70 | Error *local_err = NULL; |
71 | pid_t pid; | |
3674838c | 72 | int status; |
e3d4d252 MR |
73 | |
74 | slog("guest-shutdown called, mode: %s", mode); | |
75 | if (!has_mode || strcmp(mode, "powerdown") == 0) { | |
76 | shutdown_flag = "-P"; | |
77 | } else if (strcmp(mode, "halt") == 0) { | |
78 | shutdown_flag = "-H"; | |
79 | } else if (strcmp(mode, "reboot") == 0) { | |
80 | shutdown_flag = "-r"; | |
81 | } else { | |
d220a6df LC |
82 | error_setg(err, |
83 | "mode is invalid (valid values are: halt|powerdown|reboot"); | |
e3d4d252 MR |
84 | return; |
85 | } | |
86 | ||
d5dd3498 LC |
87 | pid = fork(); |
88 | if (pid == 0) { | |
e3d4d252 MR |
89 | /* child, start the shutdown */ |
90 | setsid(); | |
3674838c LC |
91 | reopen_fd_to_null(0); |
92 | reopen_fd_to_null(1); | |
93 | reopen_fd_to_null(2); | |
e3d4d252 | 94 | |
3674838c LC |
95 | execle("/sbin/shutdown", "shutdown", shutdown_flag, "+0", |
96 | "hypervisor initiated shutdown", (char*)NULL, environ); | |
97 | _exit(EXIT_FAILURE); | |
d5dd3498 | 98 | } else if (pid < 0) { |
d220a6df LC |
99 | error_setg_errno(err, errno, "failed to create child process"); |
100 | return; | |
e3d4d252 | 101 | } |
d5dd3498 | 102 | |
d220a6df LC |
103 | ga_wait_child(pid, &status, &local_err); |
104 | if (error_is_set(&local_err)) { | |
105 | error_propagate(err, local_err); | |
106 | return; | |
107 | } | |
108 | ||
109 | if (!WIFEXITED(status)) { | |
110 | error_setg(err, "child process has terminated abnormally"); | |
111 | return; | |
112 | } | |
113 | ||
114 | if (WEXITSTATUS(status)) { | |
115 | error_setg(err, "child process has failed to shutdown"); | |
d5dd3498 LC |
116 | return; |
117 | } | |
118 | ||
d220a6df | 119 | /* succeded */ |
e3d4d252 MR |
120 | } |
121 | ||
6912e6a9 LL |
122 | int64_t qmp_guest_get_time(Error **errp) |
123 | { | |
124 | int ret; | |
125 | qemu_timeval tq; | |
126 | int64_t time_ns; | |
127 | ||
128 | ret = qemu_gettimeofday(&tq); | |
129 | if (ret < 0) { | |
130 | error_setg_errno(errp, errno, "Failed to get time"); | |
131 | return -1; | |
132 | } | |
133 | ||
134 | time_ns = tq.tv_sec * 1000000000LL + tq.tv_usec * 1000; | |
135 | return time_ns; | |
136 | } | |
137 | ||
a1bca57f LL |
138 | void qmp_guest_set_time(int64_t time_ns, Error **errp) |
139 | { | |
140 | int ret; | |
141 | int status; | |
142 | pid_t pid; | |
143 | Error *local_err = NULL; | |
144 | struct timeval tv; | |
145 | ||
146 | /* year-2038 will overflow in case time_t is 32bit */ | |
147 | if (time_ns / 1000000000 != (time_t)(time_ns / 1000000000)) { | |
148 | error_setg(errp, "Time %" PRId64 " is too large", time_ns); | |
149 | return; | |
150 | } | |
151 | ||
152 | tv.tv_sec = time_ns / 1000000000; | |
153 | tv.tv_usec = (time_ns % 1000000000) / 1000; | |
154 | ||
155 | ret = settimeofday(&tv, NULL); | |
156 | if (ret < 0) { | |
157 | error_setg_errno(errp, errno, "Failed to set time to guest"); | |
158 | return; | |
159 | } | |
160 | ||
161 | /* Set the Hardware Clock to the current System Time. */ | |
162 | pid = fork(); | |
163 | if (pid == 0) { | |
164 | setsid(); | |
165 | reopen_fd_to_null(0); | |
166 | reopen_fd_to_null(1); | |
167 | reopen_fd_to_null(2); | |
168 | ||
169 | execle("/sbin/hwclock", "hwclock", "-w", NULL, environ); | |
170 | _exit(EXIT_FAILURE); | |
171 | } else if (pid < 0) { | |
172 | error_setg_errno(errp, errno, "failed to create child process"); | |
173 | return; | |
174 | } | |
175 | ||
176 | ga_wait_child(pid, &status, &local_err); | |
177 | if (error_is_set(&local_err)) { | |
178 | error_propagate(errp, local_err); | |
179 | return; | |
180 | } | |
181 | ||
182 | if (!WIFEXITED(status)) { | |
183 | error_setg(errp, "child process has terminated abnormally"); | |
184 | return; | |
185 | } | |
186 | ||
187 | if (WEXITSTATUS(status)) { | |
188 | error_setg(errp, "hwclock failed to set hardware clock to system time"); | |
189 | return; | |
190 | } | |
191 | } | |
192 | ||
e3d4d252 MR |
193 | typedef struct GuestFileHandle { |
194 | uint64_t id; | |
195 | FILE *fh; | |
196 | QTAILQ_ENTRY(GuestFileHandle) next; | |
197 | } GuestFileHandle; | |
198 | ||
199 | static struct { | |
200 | QTAILQ_HEAD(, GuestFileHandle) filehandles; | |
201 | } guest_file_state; | |
202 | ||
39097daf | 203 | static int64_t guest_file_handle_add(FILE *fh, Error **errp) |
e3d4d252 MR |
204 | { |
205 | GuestFileHandle *gfh; | |
39097daf MR |
206 | int64_t handle; |
207 | ||
208 | handle = ga_get_fd_handle(ga_state, errp); | |
209 | if (error_is_set(errp)) { | |
210 | return 0; | |
211 | } | |
e3d4d252 | 212 | |
7267c094 | 213 | gfh = g_malloc0(sizeof(GuestFileHandle)); |
39097daf | 214 | gfh->id = handle; |
e3d4d252 MR |
215 | gfh->fh = fh; |
216 | QTAILQ_INSERT_TAIL(&guest_file_state.filehandles, gfh, next); | |
39097daf MR |
217 | |
218 | return handle; | |
e3d4d252 MR |
219 | } |
220 | ||
a9de6d01 | 221 | static GuestFileHandle *guest_file_handle_find(int64_t id, Error **err) |
e3d4d252 MR |
222 | { |
223 | GuestFileHandle *gfh; | |
224 | ||
225 | QTAILQ_FOREACH(gfh, &guest_file_state.filehandles, next) | |
226 | { | |
227 | if (gfh->id == id) { | |
228 | return gfh; | |
229 | } | |
230 | } | |
231 | ||
a9de6d01 | 232 | error_setg(err, "handle '%" PRId64 "' has not been found", id); |
e3d4d252 MR |
233 | return NULL; |
234 | } | |
235 | ||
236 | int64_t qmp_guest_file_open(const char *path, bool has_mode, const char *mode, Error **err) | |
237 | { | |
238 | FILE *fh; | |
239 | int fd; | |
39097daf | 240 | int64_t ret = -1, handle; |
e3d4d252 MR |
241 | |
242 | if (!has_mode) { | |
243 | mode = "r"; | |
244 | } | |
245 | slog("guest-file-open called, filepath: %s, mode: %s", path, mode); | |
246 | fh = fopen(path, mode); | |
247 | if (!fh) { | |
db3edb66 LC |
248 | error_setg_errno(err, errno, "failed to open file '%s' (mode: '%s')", |
249 | path, mode); | |
e3d4d252 MR |
250 | return -1; |
251 | } | |
252 | ||
253 | /* set fd non-blocking to avoid common use cases (like reading from a | |
254 | * named pipe) from hanging the agent | |
255 | */ | |
256 | fd = fileno(fh); | |
257 | ret = fcntl(fd, F_GETFL); | |
258 | ret = fcntl(fd, F_SETFL, ret | O_NONBLOCK); | |
259 | if (ret == -1) { | |
db3edb66 LC |
260 | error_setg_errno(err, errno, "failed to make file '%s' non-blocking", |
261 | path); | |
e3d4d252 MR |
262 | fclose(fh); |
263 | return -1; | |
264 | } | |
265 | ||
39097daf MR |
266 | handle = guest_file_handle_add(fh, err); |
267 | if (error_is_set(err)) { | |
268 | fclose(fh); | |
269 | return -1; | |
270 | } | |
271 | ||
272 | slog("guest-file-open, handle: %d", handle); | |
273 | return handle; | |
e3d4d252 MR |
274 | } |
275 | ||
276 | void qmp_guest_file_close(int64_t handle, Error **err) | |
277 | { | |
a9de6d01 | 278 | GuestFileHandle *gfh = guest_file_handle_find(handle, err); |
e3d4d252 MR |
279 | int ret; |
280 | ||
281 | slog("guest-file-close called, handle: %ld", handle); | |
282 | if (!gfh) { | |
e3d4d252 MR |
283 | return; |
284 | } | |
285 | ||
286 | ret = fclose(gfh->fh); | |
3ac4b7c5 | 287 | if (ret == EOF) { |
db3edb66 | 288 | error_setg_errno(err, errno, "failed to close handle"); |
e3d4d252 MR |
289 | return; |
290 | } | |
291 | ||
292 | QTAILQ_REMOVE(&guest_file_state.filehandles, gfh, next); | |
7267c094 | 293 | g_free(gfh); |
e3d4d252 MR |
294 | } |
295 | ||
296 | struct GuestFileRead *qmp_guest_file_read(int64_t handle, bool has_count, | |
297 | int64_t count, Error **err) | |
298 | { | |
a9de6d01 | 299 | GuestFileHandle *gfh = guest_file_handle_find(handle, err); |
e3d4d252 MR |
300 | GuestFileRead *read_data = NULL; |
301 | guchar *buf; | |
302 | FILE *fh; | |
303 | size_t read_count; | |
304 | ||
305 | if (!gfh) { | |
e3d4d252 MR |
306 | return NULL; |
307 | } | |
308 | ||
309 | if (!has_count) { | |
310 | count = QGA_READ_COUNT_DEFAULT; | |
311 | } else if (count < 0) { | |
db3edb66 LC |
312 | error_setg(err, "value '%" PRId64 "' is invalid for argument count", |
313 | count); | |
e3d4d252 MR |
314 | return NULL; |
315 | } | |
316 | ||
317 | fh = gfh->fh; | |
7267c094 | 318 | buf = g_malloc0(count+1); |
e3d4d252 MR |
319 | read_count = fread(buf, 1, count, fh); |
320 | if (ferror(fh)) { | |
db3edb66 | 321 | error_setg_errno(err, errno, "failed to read file"); |
e3d4d252 | 322 | slog("guest-file-read failed, handle: %ld", handle); |
e3d4d252 MR |
323 | } else { |
324 | buf[read_count] = 0; | |
7267c094 | 325 | read_data = g_malloc0(sizeof(GuestFileRead)); |
e3d4d252 MR |
326 | read_data->count = read_count; |
327 | read_data->eof = feof(fh); | |
328 | if (read_count) { | |
329 | read_data->buf_b64 = g_base64_encode(buf, read_count); | |
330 | } | |
331 | } | |
7267c094 | 332 | g_free(buf); |
e3d4d252 MR |
333 | clearerr(fh); |
334 | ||
335 | return read_data; | |
336 | } | |
337 | ||
338 | GuestFileWrite *qmp_guest_file_write(int64_t handle, const char *buf_b64, | |
339 | bool has_count, int64_t count, Error **err) | |
340 | { | |
341 | GuestFileWrite *write_data = NULL; | |
342 | guchar *buf; | |
343 | gsize buf_len; | |
344 | int write_count; | |
a9de6d01 | 345 | GuestFileHandle *gfh = guest_file_handle_find(handle, err); |
e3d4d252 MR |
346 | FILE *fh; |
347 | ||
348 | if (!gfh) { | |
e3d4d252 MR |
349 | return NULL; |
350 | } | |
351 | ||
352 | fh = gfh->fh; | |
353 | buf = g_base64_decode(buf_b64, &buf_len); | |
354 | ||
355 | if (!has_count) { | |
356 | count = buf_len; | |
357 | } else if (count < 0 || count > buf_len) { | |
db3edb66 LC |
358 | error_setg(err, "value '%" PRId64 "' is invalid for argument count", |
359 | count); | |
7267c094 | 360 | g_free(buf); |
e3d4d252 MR |
361 | return NULL; |
362 | } | |
363 | ||
364 | write_count = fwrite(buf, 1, count, fh); | |
365 | if (ferror(fh)) { | |
db3edb66 | 366 | error_setg_errno(err, errno, "failed to write to file"); |
e3d4d252 | 367 | slog("guest-file-write failed, handle: %ld", handle); |
e3d4d252 | 368 | } else { |
7267c094 | 369 | write_data = g_malloc0(sizeof(GuestFileWrite)); |
e3d4d252 MR |
370 | write_data->count = write_count; |
371 | write_data->eof = feof(fh); | |
372 | } | |
7267c094 | 373 | g_free(buf); |
e3d4d252 MR |
374 | clearerr(fh); |
375 | ||
376 | return write_data; | |
377 | } | |
378 | ||
379 | struct GuestFileSeek *qmp_guest_file_seek(int64_t handle, int64_t offset, | |
380 | int64_t whence, Error **err) | |
381 | { | |
a9de6d01 | 382 | GuestFileHandle *gfh = guest_file_handle_find(handle, err); |
e3d4d252 MR |
383 | GuestFileSeek *seek_data = NULL; |
384 | FILE *fh; | |
385 | int ret; | |
386 | ||
387 | if (!gfh) { | |
e3d4d252 MR |
388 | return NULL; |
389 | } | |
390 | ||
391 | fh = gfh->fh; | |
392 | ret = fseek(fh, offset, whence); | |
393 | if (ret == -1) { | |
db3edb66 | 394 | error_setg_errno(err, errno, "failed to seek file"); |
e3d4d252 | 395 | } else { |
7267c094 | 396 | seek_data = g_malloc0(sizeof(GuestFileRead)); |
e3d4d252 MR |
397 | seek_data->position = ftell(fh); |
398 | seek_data->eof = feof(fh); | |
399 | } | |
400 | clearerr(fh); | |
401 | ||
402 | return seek_data; | |
403 | } | |
404 | ||
405 | void qmp_guest_file_flush(int64_t handle, Error **err) | |
406 | { | |
a9de6d01 | 407 | GuestFileHandle *gfh = guest_file_handle_find(handle, err); |
e3d4d252 MR |
408 | FILE *fh; |
409 | int ret; | |
410 | ||
411 | if (!gfh) { | |
e3d4d252 MR |
412 | return; |
413 | } | |
414 | ||
415 | fh = gfh->fh; | |
416 | ret = fflush(fh); | |
417 | if (ret == EOF) { | |
db3edb66 | 418 | error_setg_errno(err, errno, "failed to flush file"); |
e3d4d252 MR |
419 | } |
420 | } | |
421 | ||
422 | static void guest_file_init(void) | |
423 | { | |
424 | QTAILQ_INIT(&guest_file_state.filehandles); | |
425 | } | |
426 | ||
e72c3f2e MR |
427 | /* linux-specific implementations. avoid this if at all possible. */ |
428 | #if defined(__linux__) | |
429 | ||
eab5fd59 | 430 | #if defined(CONFIG_FSFREEZE) || defined(CONFIG_FSTRIM) |
af02203f | 431 | typedef struct FsMount { |
e3d4d252 MR |
432 | char *dirname; |
433 | char *devtype; | |
af02203f PB |
434 | QTAILQ_ENTRY(FsMount) next; |
435 | } FsMount; | |
e3d4d252 | 436 | |
af02203f | 437 | typedef QTAILQ_HEAD(, FsMount) FsMountList; |
9e8aded4 | 438 | |
af02203f | 439 | static void free_fs_mount_list(FsMountList *mounts) |
9e8aded4 | 440 | { |
af02203f | 441 | FsMount *mount, *temp; |
9e8aded4 MR |
442 | |
443 | if (!mounts) { | |
444 | return; | |
445 | } | |
446 | ||
447 | QTAILQ_FOREACH_SAFE(mount, mounts, next, temp) { | |
448 | QTAILQ_REMOVE(mounts, mount, next); | |
449 | g_free(mount->dirname); | |
450 | g_free(mount->devtype); | |
451 | g_free(mount); | |
452 | } | |
453 | } | |
454 | ||
e3d4d252 MR |
455 | /* |
456 | * Walk the mount table and build a list of local file systems | |
457 | */ | |
261551d1 | 458 | static void build_fs_mount_list(FsMountList *mounts, Error **err) |
e3d4d252 MR |
459 | { |
460 | struct mntent *ment; | |
af02203f | 461 | FsMount *mount; |
9e2fa418 | 462 | char const *mtab = "/proc/self/mounts"; |
e3d4d252 MR |
463 | FILE *fp; |
464 | ||
e3d4d252 MR |
465 | fp = setmntent(mtab, "r"); |
466 | if (!fp) { | |
261551d1 LC |
467 | error_setg(err, "failed to open mtab file: '%s'", mtab); |
468 | return; | |
e3d4d252 MR |
469 | } |
470 | ||
471 | while ((ment = getmntent(fp))) { | |
472 | /* | |
473 | * An entry which device name doesn't start with a '/' is | |
474 | * either a dummy file system or a network file system. | |
475 | * Add special handling for smbfs and cifs as is done by | |
476 | * coreutils as well. | |
477 | */ | |
478 | if ((ment->mnt_fsname[0] != '/') || | |
479 | (strcmp(ment->mnt_type, "smbfs") == 0) || | |
480 | (strcmp(ment->mnt_type, "cifs") == 0)) { | |
481 | continue; | |
482 | } | |
483 | ||
af02203f | 484 | mount = g_malloc0(sizeof(FsMount)); |
7267c094 AL |
485 | mount->dirname = g_strdup(ment->mnt_dir); |
486 | mount->devtype = g_strdup(ment->mnt_type); | |
e3d4d252 | 487 | |
9e8aded4 | 488 | QTAILQ_INSERT_TAIL(mounts, mount, next); |
e3d4d252 MR |
489 | } |
490 | ||
491 | endmntent(fp); | |
e3d4d252 | 492 | } |
eab5fd59 PB |
493 | #endif |
494 | ||
495 | #if defined(CONFIG_FSFREEZE) | |
e3d4d252 | 496 | |
ec0f694c TS |
497 | typedef enum { |
498 | FSFREEZE_HOOK_THAW = 0, | |
499 | FSFREEZE_HOOK_FREEZE, | |
500 | } FsfreezeHookArg; | |
501 | ||
502 | const char *fsfreeze_hook_arg_string[] = { | |
503 | "thaw", | |
504 | "freeze", | |
505 | }; | |
506 | ||
507 | static void execute_fsfreeze_hook(FsfreezeHookArg arg, Error **err) | |
508 | { | |
509 | int status; | |
510 | pid_t pid; | |
511 | const char *hook; | |
512 | const char *arg_str = fsfreeze_hook_arg_string[arg]; | |
513 | Error *local_err = NULL; | |
514 | ||
515 | hook = ga_fsfreeze_hook(ga_state); | |
516 | if (!hook) { | |
517 | return; | |
518 | } | |
519 | if (access(hook, X_OK) != 0) { | |
520 | error_setg_errno(err, errno, "can't access fsfreeze hook '%s'", hook); | |
521 | return; | |
522 | } | |
523 | ||
524 | slog("executing fsfreeze hook with arg '%s'", arg_str); | |
525 | pid = fork(); | |
526 | if (pid == 0) { | |
527 | setsid(); | |
528 | reopen_fd_to_null(0); | |
529 | reopen_fd_to_null(1); | |
530 | reopen_fd_to_null(2); | |
531 | ||
532 | execle(hook, hook, arg_str, NULL, environ); | |
533 | _exit(EXIT_FAILURE); | |
534 | } else if (pid < 0) { | |
535 | error_setg_errno(err, errno, "failed to create child process"); | |
536 | return; | |
537 | } | |
538 | ||
539 | ga_wait_child(pid, &status, &local_err); | |
540 | if (error_is_set(&local_err)) { | |
541 | error_propagate(err, local_err); | |
542 | return; | |
543 | } | |
544 | ||
545 | if (!WIFEXITED(status)) { | |
546 | error_setg(err, "fsfreeze hook has terminated abnormally"); | |
547 | return; | |
548 | } | |
549 | ||
550 | status = WEXITSTATUS(status); | |
551 | if (status) { | |
552 | error_setg(err, "fsfreeze hook has failed with status %d", status); | |
553 | return; | |
554 | } | |
555 | } | |
556 | ||
e3d4d252 MR |
557 | /* |
558 | * Return status of freeze/thaw | |
559 | */ | |
560 | GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **err) | |
561 | { | |
f22d85e9 MR |
562 | if (ga_is_frozen(ga_state)) { |
563 | return GUEST_FSFREEZE_STATUS_FROZEN; | |
564 | } | |
565 | ||
566 | return GUEST_FSFREEZE_STATUS_THAWED; | |
e3d4d252 MR |
567 | } |
568 | ||
569 | /* | |
570 | * Walk list of mounted file systems in the guest, and freeze the ones which | |
571 | * are real local file systems. | |
572 | */ | |
573 | int64_t qmp_guest_fsfreeze_freeze(Error **err) | |
574 | { | |
575 | int ret = 0, i = 0; | |
af02203f PB |
576 | FsMountList mounts; |
577 | struct FsMount *mount; | |
261551d1 | 578 | Error *local_err = NULL; |
e3d4d252 | 579 | int fd; |
e3d4d252 MR |
580 | |
581 | slog("guest-fsfreeze called"); | |
582 | ||
ec0f694c TS |
583 | execute_fsfreeze_hook(FSFREEZE_HOOK_FREEZE, &local_err); |
584 | if (error_is_set(&local_err)) { | |
585 | error_propagate(err, local_err); | |
586 | return -1; | |
587 | } | |
588 | ||
9e8aded4 | 589 | QTAILQ_INIT(&mounts); |
261551d1 LC |
590 | build_fs_mount_list(&mounts, &local_err); |
591 | if (error_is_set(&local_err)) { | |
592 | error_propagate(err, local_err); | |
593 | return -1; | |
e3d4d252 MR |
594 | } |
595 | ||
596 | /* cannot risk guest agent blocking itself on a write in this state */ | |
f22d85e9 | 597 | ga_set_frozen(ga_state); |
e3d4d252 | 598 | |
9e8aded4 | 599 | QTAILQ_FOREACH(mount, &mounts, next) { |
e3d4d252 MR |
600 | fd = qemu_open(mount->dirname, O_RDONLY); |
601 | if (fd == -1) { | |
617fbbc1 | 602 | error_setg_errno(err, errno, "failed to open %s", mount->dirname); |
e3d4d252 MR |
603 | goto error; |
604 | } | |
605 | ||
606 | /* we try to cull filesytems we know won't work in advance, but other | |
607 | * filesytems may not implement fsfreeze for less obvious reasons. | |
9e8aded4 MR |
608 | * these will report EOPNOTSUPP. we simply ignore these when tallying |
609 | * the number of frozen filesystems. | |
610 | * | |
611 | * any other error means a failure to freeze a filesystem we | |
612 | * expect to be freezable, so return an error in those cases | |
613 | * and return system to thawed state. | |
e3d4d252 MR |
614 | */ |
615 | ret = ioctl(fd, FIFREEZE); | |
9e8aded4 MR |
616 | if (ret == -1) { |
617 | if (errno != EOPNOTSUPP) { | |
617fbbc1 LC |
618 | error_setg_errno(err, errno, "failed to freeze %s", |
619 | mount->dirname); | |
9e8aded4 MR |
620 | close(fd); |
621 | goto error; | |
622 | } | |
623 | } else { | |
624 | i++; | |
e3d4d252 MR |
625 | } |
626 | close(fd); | |
e3d4d252 MR |
627 | } |
628 | ||
af02203f | 629 | free_fs_mount_list(&mounts); |
e3d4d252 MR |
630 | return i; |
631 | ||
632 | error: | |
af02203f | 633 | free_fs_mount_list(&mounts); |
9e8aded4 | 634 | qmp_guest_fsfreeze_thaw(NULL); |
e3d4d252 MR |
635 | return 0; |
636 | } | |
637 | ||
638 | /* | |
639 | * Walk list of frozen file systems in the guest, and thaw them. | |
640 | */ | |
641 | int64_t qmp_guest_fsfreeze_thaw(Error **err) | |
642 | { | |
643 | int ret; | |
af02203f PB |
644 | FsMountList mounts; |
645 | FsMount *mount; | |
9e8aded4 | 646 | int fd, i = 0, logged; |
261551d1 | 647 | Error *local_err = NULL; |
9e8aded4 MR |
648 | |
649 | QTAILQ_INIT(&mounts); | |
261551d1 LC |
650 | build_fs_mount_list(&mounts, &local_err); |
651 | if (error_is_set(&local_err)) { | |
652 | error_propagate(err, local_err); | |
9e8aded4 MR |
653 | return 0; |
654 | } | |
e3d4d252 | 655 | |
9e8aded4 MR |
656 | QTAILQ_FOREACH(mount, &mounts, next) { |
657 | logged = false; | |
e3d4d252 MR |
658 | fd = qemu_open(mount->dirname, O_RDONLY); |
659 | if (fd == -1) { | |
e3d4d252 MR |
660 | continue; |
661 | } | |
9e8aded4 MR |
662 | /* we have no way of knowing whether a filesystem was actually unfrozen |
663 | * as a result of a successful call to FITHAW, only that if an error | |
664 | * was returned the filesystem was *not* unfrozen by that particular | |
665 | * call. | |
666 | * | |
a31f0531 | 667 | * since multiple preceding FIFREEZEs require multiple calls to FITHAW |
9e8aded4 MR |
668 | * to unfreeze, continuing issuing FITHAW until an error is returned, |
669 | * in which case either the filesystem is in an unfreezable state, or, | |
670 | * more likely, it was thawed previously (and remains so afterward). | |
671 | * | |
672 | * also, since the most recent successful call is the one that did | |
673 | * the actual unfreeze, we can use this to provide an accurate count | |
674 | * of the number of filesystems unfrozen by guest-fsfreeze-thaw, which | |
675 | * may * be useful for determining whether a filesystem was unfrozen | |
676 | * during the freeze/thaw phase by a process other than qemu-ga. | |
677 | */ | |
678 | do { | |
679 | ret = ioctl(fd, FITHAW); | |
680 | if (ret == 0 && !logged) { | |
681 | i++; | |
682 | logged = true; | |
683 | } | |
684 | } while (ret == 0); | |
e3d4d252 | 685 | close(fd); |
e3d4d252 MR |
686 | } |
687 | ||
f22d85e9 | 688 | ga_unset_frozen(ga_state); |
af02203f | 689 | free_fs_mount_list(&mounts); |
ec0f694c TS |
690 | |
691 | execute_fsfreeze_hook(FSFREEZE_HOOK_THAW, err); | |
692 | ||
e3d4d252 MR |
693 | return i; |
694 | } | |
695 | ||
e3d4d252 MR |
696 | static void guest_fsfreeze_cleanup(void) |
697 | { | |
e3d4d252 MR |
698 | Error *err = NULL; |
699 | ||
f22d85e9 | 700 | if (ga_is_frozen(ga_state) == GUEST_FSFREEZE_STATUS_FROZEN) { |
6f686749 MA |
701 | qmp_guest_fsfreeze_thaw(&err); |
702 | if (err) { | |
703 | slog("failed to clean up frozen filesystems: %s", | |
704 | error_get_pretty(err)); | |
705 | error_free(err); | |
e3d4d252 MR |
706 | } |
707 | } | |
708 | } | |
e72c3f2e | 709 | #endif /* CONFIG_FSFREEZE */ |
e3d4d252 | 710 | |
eab5fd59 PB |
711 | #if defined(CONFIG_FSTRIM) |
712 | /* | |
713 | * Walk list of mounted file systems in the guest, and trim them. | |
714 | */ | |
715 | void qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **err) | |
716 | { | |
717 | int ret = 0; | |
718 | FsMountList mounts; | |
719 | struct FsMount *mount; | |
720 | int fd; | |
261551d1 | 721 | Error *local_err = NULL; |
eab5fd59 PB |
722 | struct fstrim_range r = { |
723 | .start = 0, | |
724 | .len = -1, | |
725 | .minlen = has_minimum ? minimum : 0, | |
726 | }; | |
727 | ||
728 | slog("guest-fstrim called"); | |
729 | ||
730 | QTAILQ_INIT(&mounts); | |
261551d1 LC |
731 | build_fs_mount_list(&mounts, &local_err); |
732 | if (error_is_set(&local_err)) { | |
733 | error_propagate(err, local_err); | |
eab5fd59 PB |
734 | return; |
735 | } | |
736 | ||
737 | QTAILQ_FOREACH(mount, &mounts, next) { | |
738 | fd = qemu_open(mount->dirname, O_RDONLY); | |
739 | if (fd == -1) { | |
071673b0 | 740 | error_setg_errno(err, errno, "failed to open %s", mount->dirname); |
eab5fd59 PB |
741 | goto error; |
742 | } | |
743 | ||
744 | /* We try to cull filesytems we know won't work in advance, but other | |
745 | * filesytems may not implement fstrim for less obvious reasons. These | |
746 | * will report EOPNOTSUPP; we simply ignore these errors. Any other | |
747 | * error means an unexpected error, so return it in those cases. In | |
748 | * some other cases ENOTTY will be reported (e.g. CD-ROMs). | |
749 | */ | |
750 | ret = ioctl(fd, FITRIM, &r); | |
751 | if (ret == -1) { | |
752 | if (errno != ENOTTY && errno != EOPNOTSUPP) { | |
071673b0 LC |
753 | error_setg_errno(err, errno, "failed to trim %s", |
754 | mount->dirname); | |
eab5fd59 PB |
755 | close(fd); |
756 | goto error; | |
757 | } | |
758 | } | |
759 | close(fd); | |
760 | } | |
761 | ||
762 | error: | |
763 | free_fs_mount_list(&mounts); | |
764 | } | |
765 | #endif /* CONFIG_FSTRIM */ | |
766 | ||
767 | ||
11d0f125 LC |
768 | #define LINUX_SYS_STATE_FILE "/sys/power/state" |
769 | #define SUSPEND_SUPPORTED 0 | |
770 | #define SUSPEND_NOT_SUPPORTED 1 | |
771 | ||
11d0f125 LC |
772 | static void bios_supports_mode(const char *pmutils_bin, const char *pmutils_arg, |
773 | const char *sysfile_str, Error **err) | |
774 | { | |
6b26e837 | 775 | Error *local_err = NULL; |
11d0f125 | 776 | char *pmutils_path; |
6b26e837 | 777 | pid_t pid; |
dc8764f0 | 778 | int status; |
11d0f125 LC |
779 | |
780 | pmutils_path = g_find_program_in_path(pmutils_bin); | |
781 | ||
782 | pid = fork(); | |
783 | if (!pid) { | |
dc8764f0 LC |
784 | char buf[32]; /* hopefully big enough */ |
785 | ssize_t ret; | |
786 | int fd; | |
11d0f125 LC |
787 | |
788 | setsid(); | |
11d0f125 LC |
789 | reopen_fd_to_null(0); |
790 | reopen_fd_to_null(1); | |
791 | reopen_fd_to_null(2); | |
792 | ||
dc8764f0 LC |
793 | if (pmutils_path) { |
794 | execle(pmutils_path, pmutils_bin, pmutils_arg, NULL, environ); | |
795 | } | |
11d0f125 | 796 | |
dc8764f0 LC |
797 | /* |
798 | * If we get here either pm-utils is not installed or execle() has | |
799 | * failed. Let's try the manual method if the caller wants it. | |
800 | */ | |
11d0f125 | 801 | |
dc8764f0 LC |
802 | if (!sysfile_str) { |
803 | _exit(SUSPEND_NOT_SUPPORTED); | |
804 | } | |
11d0f125 | 805 | |
dc8764f0 LC |
806 | fd = open(LINUX_SYS_STATE_FILE, O_RDONLY); |
807 | if (fd < 0) { | |
11d0f125 LC |
808 | _exit(SUSPEND_NOT_SUPPORTED); |
809 | } | |
810 | ||
dc8764f0 LC |
811 | ret = read(fd, buf, sizeof(buf)-1); |
812 | if (ret <= 0) { | |
813 | _exit(SUSPEND_NOT_SUPPORTED); | |
11d0f125 | 814 | } |
dc8764f0 | 815 | buf[ret] = '\0'; |
11d0f125 | 816 | |
dc8764f0 LC |
817 | if (strstr(buf, sysfile_str)) { |
818 | _exit(SUSPEND_SUPPORTED); | |
11d0f125 LC |
819 | } |
820 | ||
dc8764f0 | 821 | _exit(SUSPEND_NOT_SUPPORTED); |
6b26e837 LC |
822 | } else if (pid < 0) { |
823 | error_setg_errno(err, errno, "failed to create child process"); | |
824 | goto out; | |
11d0f125 LC |
825 | } |
826 | ||
6b26e837 LC |
827 | ga_wait_child(pid, &status, &local_err); |
828 | if (error_is_set(&local_err)) { | |
829 | error_propagate(err, local_err); | |
830 | goto out; | |
831 | } | |
11d0f125 | 832 | |
6b26e837 LC |
833 | if (!WIFEXITED(status)) { |
834 | error_setg(err, "child process has terminated abnormally"); | |
835 | goto out; | |
dc8764f0 LC |
836 | } |
837 | ||
6b26e837 LC |
838 | switch (WEXITSTATUS(status)) { |
839 | case SUSPEND_SUPPORTED: | |
840 | goto out; | |
841 | case SUSPEND_NOT_SUPPORTED: | |
842 | error_setg(err, | |
843 | "the requested suspend mode is not supported by the guest"); | |
844 | goto out; | |
845 | default: | |
846 | error_setg(err, | |
847 | "the helper program '%s' returned an unexpected exit status" | |
848 | " code (%d)", pmutils_path, WEXITSTATUS(status)); | |
849 | goto out; | |
11d0f125 LC |
850 | } |
851 | ||
6b26e837 LC |
852 | out: |
853 | g_free(pmutils_path); | |
11d0f125 LC |
854 | } |
855 | ||
856 | static void guest_suspend(const char *pmutils_bin, const char *sysfile_str, | |
857 | Error **err) | |
858 | { | |
7b376087 | 859 | Error *local_err = NULL; |
11d0f125 | 860 | char *pmutils_path; |
7b376087 | 861 | pid_t pid; |
dc8764f0 | 862 | int status; |
11d0f125 LC |
863 | |
864 | pmutils_path = g_find_program_in_path(pmutils_bin); | |
865 | ||
866 | pid = fork(); | |
867 | if (pid == 0) { | |
868 | /* child */ | |
869 | int fd; | |
870 | ||
871 | setsid(); | |
872 | reopen_fd_to_null(0); | |
873 | reopen_fd_to_null(1); | |
874 | reopen_fd_to_null(2); | |
875 | ||
876 | if (pmutils_path) { | |
877 | execle(pmutils_path, pmutils_bin, NULL, environ); | |
878 | } | |
879 | ||
880 | /* | |
881 | * If we get here either pm-utils is not installed or execle() has | |
882 | * failed. Let's try the manual method if the caller wants it. | |
883 | */ | |
884 | ||
885 | if (!sysfile_str) { | |
886 | _exit(EXIT_FAILURE); | |
887 | } | |
888 | ||
889 | fd = open(LINUX_SYS_STATE_FILE, O_WRONLY); | |
890 | if (fd < 0) { | |
891 | _exit(EXIT_FAILURE); | |
892 | } | |
893 | ||
894 | if (write(fd, sysfile_str, strlen(sysfile_str)) < 0) { | |
895 | _exit(EXIT_FAILURE); | |
896 | } | |
897 | ||
898 | _exit(EXIT_SUCCESS); | |
7b376087 LC |
899 | } else if (pid < 0) { |
900 | error_setg_errno(err, errno, "failed to create child process"); | |
901 | goto out; | |
11d0f125 LC |
902 | } |
903 | ||
7b376087 LC |
904 | ga_wait_child(pid, &status, &local_err); |
905 | if (error_is_set(&local_err)) { | |
906 | error_propagate(err, local_err); | |
907 | goto out; | |
908 | } | |
11d0f125 | 909 | |
7b376087 LC |
910 | if (!WIFEXITED(status)) { |
911 | error_setg(err, "child process has terminated abnormally"); | |
912 | goto out; | |
dc8764f0 LC |
913 | } |
914 | ||
7b376087 LC |
915 | if (WEXITSTATUS(status)) { |
916 | error_setg(err, "child process has failed to suspend"); | |
917 | goto out; | |
11d0f125 | 918 | } |
dc8764f0 | 919 | |
7b376087 LC |
920 | out: |
921 | g_free(pmutils_path); | |
11d0f125 LC |
922 | } |
923 | ||
924 | void qmp_guest_suspend_disk(Error **err) | |
925 | { | |
926 | bios_supports_mode("pm-is-supported", "--hibernate", "disk", err); | |
927 | if (error_is_set(err)) { | |
928 | return; | |
929 | } | |
930 | ||
931 | guest_suspend("pm-hibernate", "disk", err); | |
932 | } | |
933 | ||
fbf42210 LC |
934 | void qmp_guest_suspend_ram(Error **err) |
935 | { | |
936 | bios_supports_mode("pm-is-supported", "--suspend", "mem", err); | |
937 | if (error_is_set(err)) { | |
938 | return; | |
939 | } | |
940 | ||
941 | guest_suspend("pm-suspend", "mem", err); | |
942 | } | |
943 | ||
95f4f404 LC |
944 | void qmp_guest_suspend_hybrid(Error **err) |
945 | { | |
946 | bios_supports_mode("pm-is-supported", "--suspend-hybrid", NULL, err); | |
947 | if (error_is_set(err)) { | |
948 | return; | |
949 | } | |
950 | ||
951 | guest_suspend("pm-suspend-hybrid", NULL, err); | |
952 | } | |
953 | ||
3424fc9f MP |
954 | static GuestNetworkInterfaceList * |
955 | guest_find_interface(GuestNetworkInterfaceList *head, | |
956 | const char *name) | |
957 | { | |
958 | for (; head; head = head->next) { | |
959 | if (strcmp(head->value->name, name) == 0) { | |
960 | break; | |
961 | } | |
962 | } | |
963 | ||
964 | return head; | |
965 | } | |
966 | ||
967 | /* | |
968 | * Build information about guest interfaces | |
969 | */ | |
970 | GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp) | |
971 | { | |
972 | GuestNetworkInterfaceList *head = NULL, *cur_item = NULL; | |
973 | struct ifaddrs *ifap, *ifa; | |
3424fc9f MP |
974 | |
975 | if (getifaddrs(&ifap) < 0) { | |
878a0ae0 | 976 | error_setg_errno(errp, errno, "getifaddrs failed"); |
3424fc9f MP |
977 | goto error; |
978 | } | |
979 | ||
980 | for (ifa = ifap; ifa; ifa = ifa->ifa_next) { | |
981 | GuestNetworkInterfaceList *info; | |
982 | GuestIpAddressList **address_list = NULL, *address_item = NULL; | |
983 | char addr4[INET_ADDRSTRLEN]; | |
984 | char addr6[INET6_ADDRSTRLEN]; | |
985 | int sock; | |
986 | struct ifreq ifr; | |
987 | unsigned char *mac_addr; | |
988 | void *p; | |
989 | ||
990 | g_debug("Processing %s interface", ifa->ifa_name); | |
991 | ||
992 | info = guest_find_interface(head, ifa->ifa_name); | |
993 | ||
994 | if (!info) { | |
995 | info = g_malloc0(sizeof(*info)); | |
996 | info->value = g_malloc0(sizeof(*info->value)); | |
997 | info->value->name = g_strdup(ifa->ifa_name); | |
998 | ||
999 | if (!cur_item) { | |
1000 | head = cur_item = info; | |
1001 | } else { | |
1002 | cur_item->next = info; | |
1003 | cur_item = info; | |
1004 | } | |
1005 | } | |
1006 | ||
1007 | if (!info->value->has_hardware_address && | |
1008 | ifa->ifa_flags & SIOCGIFHWADDR) { | |
1009 | /* we haven't obtained HW address yet */ | |
1010 | sock = socket(PF_INET, SOCK_STREAM, 0); | |
1011 | if (sock == -1) { | |
878a0ae0 | 1012 | error_setg_errno(errp, errno, "failed to create socket"); |
3424fc9f MP |
1013 | goto error; |
1014 | } | |
1015 | ||
1016 | memset(&ifr, 0, sizeof(ifr)); | |
1ab516ed | 1017 | pstrcpy(ifr.ifr_name, IF_NAMESIZE, info->value->name); |
3424fc9f | 1018 | if (ioctl(sock, SIOCGIFHWADDR, &ifr) == -1) { |
878a0ae0 LC |
1019 | error_setg_errno(errp, errno, |
1020 | "failed to get MAC address of %s", | |
1021 | ifa->ifa_name); | |
10a2158f | 1022 | close(sock); |
3424fc9f MP |
1023 | goto error; |
1024 | } | |
1025 | ||
10a2158f | 1026 | close(sock); |
3424fc9f MP |
1027 | mac_addr = (unsigned char *) &ifr.ifr_hwaddr.sa_data; |
1028 | ||
e4ada482 SW |
1029 | info->value->hardware_address = |
1030 | g_strdup_printf("%02x:%02x:%02x:%02x:%02x:%02x", | |
1031 | (int) mac_addr[0], (int) mac_addr[1], | |
1032 | (int) mac_addr[2], (int) mac_addr[3], | |
1033 | (int) mac_addr[4], (int) mac_addr[5]); | |
3424fc9f MP |
1034 | |
1035 | info->value->has_hardware_address = true; | |
3424fc9f MP |
1036 | } |
1037 | ||
1038 | if (ifa->ifa_addr && | |
1039 | ifa->ifa_addr->sa_family == AF_INET) { | |
1040 | /* interface with IPv4 address */ | |
3424fc9f MP |
1041 | p = &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr; |
1042 | if (!inet_ntop(AF_INET, p, addr4, sizeof(addr4))) { | |
878a0ae0 | 1043 | error_setg_errno(errp, errno, "inet_ntop failed"); |
3424fc9f MP |
1044 | goto error; |
1045 | } | |
1046 | ||
10a2158f MA |
1047 | address_item = g_malloc0(sizeof(*address_item)); |
1048 | address_item->value = g_malloc0(sizeof(*address_item->value)); | |
3424fc9f MP |
1049 | address_item->value->ip_address = g_strdup(addr4); |
1050 | address_item->value->ip_address_type = GUEST_IP_ADDRESS_TYPE_IPV4; | |
1051 | ||
1052 | if (ifa->ifa_netmask) { | |
1053 | /* Count the number of set bits in netmask. | |
1054 | * This is safe as '1' and '0' cannot be shuffled in netmask. */ | |
1055 | p = &((struct sockaddr_in *)ifa->ifa_netmask)->sin_addr; | |
1056 | address_item->value->prefix = ctpop32(((uint32_t *) p)[0]); | |
1057 | } | |
1058 | } else if (ifa->ifa_addr && | |
1059 | ifa->ifa_addr->sa_family == AF_INET6) { | |
1060 | /* interface with IPv6 address */ | |
3424fc9f MP |
1061 | p = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr; |
1062 | if (!inet_ntop(AF_INET6, p, addr6, sizeof(addr6))) { | |
878a0ae0 | 1063 | error_setg_errno(errp, errno, "inet_ntop failed"); |
3424fc9f MP |
1064 | goto error; |
1065 | } | |
1066 | ||
10a2158f MA |
1067 | address_item = g_malloc0(sizeof(*address_item)); |
1068 | address_item->value = g_malloc0(sizeof(*address_item->value)); | |
3424fc9f MP |
1069 | address_item->value->ip_address = g_strdup(addr6); |
1070 | address_item->value->ip_address_type = GUEST_IP_ADDRESS_TYPE_IPV6; | |
1071 | ||
1072 | if (ifa->ifa_netmask) { | |
1073 | /* Count the number of set bits in netmask. | |
1074 | * This is safe as '1' and '0' cannot be shuffled in netmask. */ | |
1075 | p = &((struct sockaddr_in6 *)ifa->ifa_netmask)->sin6_addr; | |
1076 | address_item->value->prefix = | |
1077 | ctpop32(((uint32_t *) p)[0]) + | |
1078 | ctpop32(((uint32_t *) p)[1]) + | |
1079 | ctpop32(((uint32_t *) p)[2]) + | |
1080 | ctpop32(((uint32_t *) p)[3]); | |
1081 | } | |
1082 | } | |
1083 | ||
1084 | if (!address_item) { | |
1085 | continue; | |
1086 | } | |
1087 | ||
1088 | address_list = &info->value->ip_addresses; | |
1089 | ||
1090 | while (*address_list && (*address_list)->next) { | |
1091 | address_list = &(*address_list)->next; | |
1092 | } | |
1093 | ||
1094 | if (!*address_list) { | |
1095 | *address_list = address_item; | |
1096 | } else { | |
1097 | (*address_list)->next = address_item; | |
1098 | } | |
1099 | ||
1100 | info->value->has_ip_addresses = true; | |
1101 | ||
1102 | ||
1103 | } | |
1104 | ||
1105 | freeifaddrs(ifap); | |
1106 | return head; | |
1107 | ||
1108 | error: | |
1109 | freeifaddrs(ifap); | |
1110 | qapi_free_GuestNetworkInterfaceList(head); | |
1111 | return NULL; | |
1112 | } | |
1113 | ||
e72c3f2e MR |
1114 | #else /* defined(__linux__) */ |
1115 | ||
d35d4cb5 | 1116 | void qmp_guest_suspend_disk(Error **err) |
e72c3f2e MR |
1117 | { |
1118 | error_set(err, QERR_UNSUPPORTED); | |
e72c3f2e MR |
1119 | } |
1120 | ||
d35d4cb5 | 1121 | void qmp_guest_suspend_ram(Error **err) |
e72c3f2e MR |
1122 | { |
1123 | error_set(err, QERR_UNSUPPORTED); | |
e72c3f2e MR |
1124 | } |
1125 | ||
d35d4cb5 | 1126 | void qmp_guest_suspend_hybrid(Error **err) |
e72c3f2e MR |
1127 | { |
1128 | error_set(err, QERR_UNSUPPORTED); | |
e72c3f2e MR |
1129 | } |
1130 | ||
d35d4cb5 | 1131 | GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp) |
e72c3f2e | 1132 | { |
d35d4cb5 MR |
1133 | error_set(errp, QERR_UNSUPPORTED); |
1134 | return NULL; | |
e72c3f2e MR |
1135 | } |
1136 | ||
d35d4cb5 MR |
1137 | #endif |
1138 | ||
1139 | #if !defined(CONFIG_FSFREEZE) | |
1140 | ||
1141 | GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **err) | |
e72c3f2e MR |
1142 | { |
1143 | error_set(err, QERR_UNSUPPORTED); | |
d35d4cb5 MR |
1144 | |
1145 | return 0; | |
e72c3f2e MR |
1146 | } |
1147 | ||
d35d4cb5 | 1148 | int64_t qmp_guest_fsfreeze_freeze(Error **err) |
e72c3f2e MR |
1149 | { |
1150 | error_set(err, QERR_UNSUPPORTED); | |
d35d4cb5 MR |
1151 | |
1152 | return 0; | |
e72c3f2e MR |
1153 | } |
1154 | ||
d35d4cb5 | 1155 | int64_t qmp_guest_fsfreeze_thaw(Error **err) |
e72c3f2e | 1156 | { |
d35d4cb5 MR |
1157 | error_set(err, QERR_UNSUPPORTED); |
1158 | ||
1159 | return 0; | |
e72c3f2e | 1160 | } |
eab5fd59 PB |
1161 | #endif /* CONFIG_FSFREEZE */ |
1162 | ||
1163 | #if !defined(CONFIG_FSTRIM) | |
1164 | void qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **err) | |
1165 | { | |
1166 | error_set(err, QERR_UNSUPPORTED); | |
eab5fd59 | 1167 | } |
e72c3f2e MR |
1168 | #endif |
1169 | ||
70e133a7 LE |
1170 | GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp) |
1171 | { | |
1172 | error_set(errp, QERR_UNSUPPORTED); | |
1173 | return NULL; | |
1174 | } | |
1175 | ||
1176 | int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp) | |
1177 | { | |
1178 | error_set(errp, QERR_UNSUPPORTED); | |
1179 | return -1; | |
1180 | } | |
1181 | ||
e3d4d252 MR |
1182 | /* register init/cleanup routines for stateful command groups */ |
1183 | void ga_command_state_init(GAState *s, GACommandState *cs) | |
1184 | { | |
7006b9cf | 1185 | #if defined(CONFIG_FSFREEZE) |
f22d85e9 | 1186 | ga_command_state_add(cs, NULL, guest_fsfreeze_cleanup); |
7006b9cf | 1187 | #endif |
e3d4d252 MR |
1188 | ga_command_state_add(cs, guest_file_init, NULL); |
1189 | } |