]> git.proxmox.com Git - qemu.git/blame - qga/commands-posix.c
qemu-ga: guest-suspend: make the API synchronous
[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>
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>
17#include "qga/guest-agent-core.h"
18#include "qga-qmp-commands.h"
19#include "qerror.h"
20#include "qemu-queue.h"
21#include "host-utils.h"
4eb36d40
AL
22
23#if defined(__linux__)
e3d4d252 24#include <mntent.h>
7006b9cf 25#include <linux/fs.h>
3424fc9f
MP
26#include <ifaddrs.h>
27#include <arpa/inet.h>
28#include <sys/socket.h>
29#include <net/if.h>
11d0f125 30#include <sys/wait.h>
e3d4d252 31
e72c3f2e
MR
32#if defined(__linux__) && defined(FIFREEZE)
33#define CONFIG_FSFREEZE
34#endif
35#endif
36
e3d4d252
MR
37void qmp_guest_shutdown(bool has_mode, const char *mode, Error **err)
38{
39 int ret;
40 const char *shutdown_flag;
41
42 slog("guest-shutdown called, mode: %s", mode);
43 if (!has_mode || strcmp(mode, "powerdown") == 0) {
44 shutdown_flag = "-P";
45 } else if (strcmp(mode, "halt") == 0) {
46 shutdown_flag = "-H";
47 } else if (strcmp(mode, "reboot") == 0) {
48 shutdown_flag = "-r";
49 } else {
50 error_set(err, QERR_INVALID_PARAMETER_VALUE, "mode",
51 "halt|powerdown|reboot");
52 return;
53 }
54
55 ret = fork();
56 if (ret == 0) {
57 /* child, start the shutdown */
58 setsid();
59 fclose(stdin);
60 fclose(stdout);
61 fclose(stderr);
62
63 ret = execl("/sbin/shutdown", "shutdown", shutdown_flag, "+0",
64 "hypervisor initiated shutdown", (char*)NULL);
65 if (ret) {
66 slog("guest-shutdown failed: %s", strerror(errno));
67 }
68 exit(!!ret);
69 } else if (ret < 0) {
70 error_set(err, QERR_UNDEFINED_ERROR);
71 }
72}
73
74typedef struct GuestFileHandle {
75 uint64_t id;
76 FILE *fh;
77 QTAILQ_ENTRY(GuestFileHandle) next;
78} GuestFileHandle;
79
80static struct {
81 QTAILQ_HEAD(, GuestFileHandle) filehandles;
82} guest_file_state;
83
84static void guest_file_handle_add(FILE *fh)
85{
86 GuestFileHandle *gfh;
87
7267c094 88 gfh = g_malloc0(sizeof(GuestFileHandle));
e3d4d252
MR
89 gfh->id = fileno(fh);
90 gfh->fh = fh;
91 QTAILQ_INSERT_TAIL(&guest_file_state.filehandles, gfh, next);
92}
93
94static GuestFileHandle *guest_file_handle_find(int64_t id)
95{
96 GuestFileHandle *gfh;
97
98 QTAILQ_FOREACH(gfh, &guest_file_state.filehandles, next)
99 {
100 if (gfh->id == id) {
101 return gfh;
102 }
103 }
104
105 return NULL;
106}
107
108int64_t qmp_guest_file_open(const char *path, bool has_mode, const char *mode, Error **err)
109{
110 FILE *fh;
111 int fd;
112 int64_t ret = -1;
113
114 if (!has_mode) {
115 mode = "r";
116 }
117 slog("guest-file-open called, filepath: %s, mode: %s", path, mode);
118 fh = fopen(path, mode);
119 if (!fh) {
120 error_set(err, QERR_OPEN_FILE_FAILED, path);
121 return -1;
122 }
123
124 /* set fd non-blocking to avoid common use cases (like reading from a
125 * named pipe) from hanging the agent
126 */
127 fd = fileno(fh);
128 ret = fcntl(fd, F_GETFL);
129 ret = fcntl(fd, F_SETFL, ret | O_NONBLOCK);
130 if (ret == -1) {
131 error_set(err, QERR_QGA_COMMAND_FAILED, "fcntl() failed");
132 fclose(fh);
133 return -1;
134 }
135
136 guest_file_handle_add(fh);
137 slog("guest-file-open, handle: %d", fd);
138 return fd;
139}
140
141void qmp_guest_file_close(int64_t handle, Error **err)
142{
143 GuestFileHandle *gfh = guest_file_handle_find(handle);
144 int ret;
145
146 slog("guest-file-close called, handle: %ld", handle);
147 if (!gfh) {
148 error_set(err, QERR_FD_NOT_FOUND, "handle");
149 return;
150 }
151
152 ret = fclose(gfh->fh);
153 if (ret == -1) {
154 error_set(err, QERR_QGA_COMMAND_FAILED, "fclose() failed");
155 return;
156 }
157
158 QTAILQ_REMOVE(&guest_file_state.filehandles, gfh, next);
7267c094 159 g_free(gfh);
e3d4d252
MR
160}
161
162struct GuestFileRead *qmp_guest_file_read(int64_t handle, bool has_count,
163 int64_t count, Error **err)
164{
165 GuestFileHandle *gfh = guest_file_handle_find(handle);
166 GuestFileRead *read_data = NULL;
167 guchar *buf;
168 FILE *fh;
169 size_t read_count;
170
171 if (!gfh) {
172 error_set(err, QERR_FD_NOT_FOUND, "handle");
173 return NULL;
174 }
175
176 if (!has_count) {
177 count = QGA_READ_COUNT_DEFAULT;
178 } else if (count < 0) {
179 error_set(err, QERR_INVALID_PARAMETER, "count");
180 return NULL;
181 }
182
183 fh = gfh->fh;
7267c094 184 buf = g_malloc0(count+1);
e3d4d252
MR
185 read_count = fread(buf, 1, count, fh);
186 if (ferror(fh)) {
187 slog("guest-file-read failed, handle: %ld", handle);
188 error_set(err, QERR_QGA_COMMAND_FAILED, "fread() failed");
189 } else {
190 buf[read_count] = 0;
7267c094 191 read_data = g_malloc0(sizeof(GuestFileRead));
e3d4d252
MR
192 read_data->count = read_count;
193 read_data->eof = feof(fh);
194 if (read_count) {
195 read_data->buf_b64 = g_base64_encode(buf, read_count);
196 }
197 }
7267c094 198 g_free(buf);
e3d4d252
MR
199 clearerr(fh);
200
201 return read_data;
202}
203
204GuestFileWrite *qmp_guest_file_write(int64_t handle, const char *buf_b64,
205 bool has_count, int64_t count, Error **err)
206{
207 GuestFileWrite *write_data = NULL;
208 guchar *buf;
209 gsize buf_len;
210 int write_count;
211 GuestFileHandle *gfh = guest_file_handle_find(handle);
212 FILE *fh;
213
214 if (!gfh) {
215 error_set(err, QERR_FD_NOT_FOUND, "handle");
216 return NULL;
217 }
218
219 fh = gfh->fh;
220 buf = g_base64_decode(buf_b64, &buf_len);
221
222 if (!has_count) {
223 count = buf_len;
224 } else if (count < 0 || count > buf_len) {
7267c094 225 g_free(buf);
e3d4d252
MR
226 error_set(err, QERR_INVALID_PARAMETER, "count");
227 return NULL;
228 }
229
230 write_count = fwrite(buf, 1, count, fh);
231 if (ferror(fh)) {
232 slog("guest-file-write failed, handle: %ld", handle);
233 error_set(err, QERR_QGA_COMMAND_FAILED, "fwrite() error");
234 } else {
7267c094 235 write_data = g_malloc0(sizeof(GuestFileWrite));
e3d4d252
MR
236 write_data->count = write_count;
237 write_data->eof = feof(fh);
238 }
7267c094 239 g_free(buf);
e3d4d252
MR
240 clearerr(fh);
241
242 return write_data;
243}
244
245struct GuestFileSeek *qmp_guest_file_seek(int64_t handle, int64_t offset,
246 int64_t whence, Error **err)
247{
248 GuestFileHandle *gfh = guest_file_handle_find(handle);
249 GuestFileSeek *seek_data = NULL;
250 FILE *fh;
251 int ret;
252
253 if (!gfh) {
254 error_set(err, QERR_FD_NOT_FOUND, "handle");
255 return NULL;
256 }
257
258 fh = gfh->fh;
259 ret = fseek(fh, offset, whence);
260 if (ret == -1) {
261 error_set(err, QERR_QGA_COMMAND_FAILED, strerror(errno));
262 } else {
7267c094 263 seek_data = g_malloc0(sizeof(GuestFileRead));
e3d4d252
MR
264 seek_data->position = ftell(fh);
265 seek_data->eof = feof(fh);
266 }
267 clearerr(fh);
268
269 return seek_data;
270}
271
272void qmp_guest_file_flush(int64_t handle, Error **err)
273{
274 GuestFileHandle *gfh = guest_file_handle_find(handle);
275 FILE *fh;
276 int ret;
277
278 if (!gfh) {
279 error_set(err, QERR_FD_NOT_FOUND, "handle");
280 return;
281 }
282
283 fh = gfh->fh;
284 ret = fflush(fh);
285 if (ret == EOF) {
286 error_set(err, QERR_QGA_COMMAND_FAILED, strerror(errno));
287 }
288}
289
290static void guest_file_init(void)
291{
292 QTAILQ_INIT(&guest_file_state.filehandles);
293}
294
e72c3f2e
MR
295/* linux-specific implementations. avoid this if at all possible. */
296#if defined(__linux__)
297
7006b9cf 298#if defined(CONFIG_FSFREEZE)
e72c3f2e 299
e3d4d252
MR
300typedef struct GuestFsfreezeMount {
301 char *dirname;
302 char *devtype;
303 QTAILQ_ENTRY(GuestFsfreezeMount) next;
304} GuestFsfreezeMount;
305
9e8aded4
MR
306typedef QTAILQ_HEAD(, GuestFsfreezeMount) GuestFsfreezeMountList;
307
9e8aded4
MR
308static void guest_fsfreeze_free_mount_list(GuestFsfreezeMountList *mounts)
309{
310 GuestFsfreezeMount *mount, *temp;
311
312 if (!mounts) {
313 return;
314 }
315
316 QTAILQ_FOREACH_SAFE(mount, mounts, next, temp) {
317 QTAILQ_REMOVE(mounts, mount, next);
318 g_free(mount->dirname);
319 g_free(mount->devtype);
320 g_free(mount);
321 }
322}
323
e3d4d252
MR
324/*
325 * Walk the mount table and build a list of local file systems
326 */
9e8aded4 327static int guest_fsfreeze_build_mount_list(GuestFsfreezeMountList *mounts)
e3d4d252
MR
328{
329 struct mntent *ment;
9e8aded4 330 GuestFsfreezeMount *mount;
e3d4d252
MR
331 char const *mtab = MOUNTED;
332 FILE *fp;
333
e3d4d252
MR
334 fp = setmntent(mtab, "r");
335 if (!fp) {
336 g_warning("fsfreeze: unable to read mtab");
337 return -1;
338 }
339
340 while ((ment = getmntent(fp))) {
341 /*
342 * An entry which device name doesn't start with a '/' is
343 * either a dummy file system or a network file system.
344 * Add special handling for smbfs and cifs as is done by
345 * coreutils as well.
346 */
347 if ((ment->mnt_fsname[0] != '/') ||
348 (strcmp(ment->mnt_type, "smbfs") == 0) ||
349 (strcmp(ment->mnt_type, "cifs") == 0)) {
350 continue;
351 }
352
7267c094
AL
353 mount = g_malloc0(sizeof(GuestFsfreezeMount));
354 mount->dirname = g_strdup(ment->mnt_dir);
355 mount->devtype = g_strdup(ment->mnt_type);
e3d4d252 356
9e8aded4 357 QTAILQ_INSERT_TAIL(mounts, mount, next);
e3d4d252
MR
358 }
359
360 endmntent(fp);
361
362 return 0;
363}
364
365/*
366 * Return status of freeze/thaw
367 */
368GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **err)
369{
f22d85e9
MR
370 if (ga_is_frozen(ga_state)) {
371 return GUEST_FSFREEZE_STATUS_FROZEN;
372 }
373
374 return GUEST_FSFREEZE_STATUS_THAWED;
e3d4d252
MR
375}
376
377/*
378 * Walk list of mounted file systems in the guest, and freeze the ones which
379 * are real local file systems.
380 */
381int64_t qmp_guest_fsfreeze_freeze(Error **err)
382{
383 int ret = 0, i = 0;
9e8aded4
MR
384 GuestFsfreezeMountList mounts;
385 struct GuestFsfreezeMount *mount;
e3d4d252
MR
386 int fd;
387 char err_msg[512];
388
389 slog("guest-fsfreeze called");
390
9e8aded4
MR
391 QTAILQ_INIT(&mounts);
392 ret = guest_fsfreeze_build_mount_list(&mounts);
e3d4d252
MR
393 if (ret < 0) {
394 return ret;
395 }
396
397 /* cannot risk guest agent blocking itself on a write in this state */
f22d85e9 398 ga_set_frozen(ga_state);
e3d4d252 399
9e8aded4 400 QTAILQ_FOREACH(mount, &mounts, next) {
e3d4d252
MR
401 fd = qemu_open(mount->dirname, O_RDONLY);
402 if (fd == -1) {
9e8aded4
MR
403 sprintf(err_msg, "failed to open %s, %s", mount->dirname,
404 strerror(errno));
e3d4d252
MR
405 error_set(err, QERR_QGA_COMMAND_FAILED, err_msg);
406 goto error;
407 }
408
409 /* we try to cull filesytems we know won't work in advance, but other
410 * filesytems may not implement fsfreeze for less obvious reasons.
9e8aded4
MR
411 * these will report EOPNOTSUPP. we simply ignore these when tallying
412 * the number of frozen filesystems.
413 *
414 * any other error means a failure to freeze a filesystem we
415 * expect to be freezable, so return an error in those cases
416 * and return system to thawed state.
e3d4d252
MR
417 */
418 ret = ioctl(fd, FIFREEZE);
9e8aded4
MR
419 if (ret == -1) {
420 if (errno != EOPNOTSUPP) {
421 sprintf(err_msg, "failed to freeze %s, %s",
422 mount->dirname, strerror(errno));
423 error_set(err, QERR_QGA_COMMAND_FAILED, err_msg);
424 close(fd);
425 goto error;
426 }
427 } else {
428 i++;
e3d4d252
MR
429 }
430 close(fd);
e3d4d252
MR
431 }
432
9e8aded4 433 guest_fsfreeze_free_mount_list(&mounts);
e3d4d252
MR
434 return i;
435
436error:
9e8aded4
MR
437 guest_fsfreeze_free_mount_list(&mounts);
438 qmp_guest_fsfreeze_thaw(NULL);
e3d4d252
MR
439 return 0;
440}
441
442/*
443 * Walk list of frozen file systems in the guest, and thaw them.
444 */
445int64_t qmp_guest_fsfreeze_thaw(Error **err)
446{
447 int ret;
9e8aded4
MR
448 GuestFsfreezeMountList mounts;
449 GuestFsfreezeMount *mount;
450 int fd, i = 0, logged;
451
452 QTAILQ_INIT(&mounts);
453 ret = guest_fsfreeze_build_mount_list(&mounts);
454 if (ret) {
455 error_set(err, QERR_QGA_COMMAND_FAILED,
456 "failed to enumerate filesystems");
457 return 0;
458 }
e3d4d252 459
9e8aded4
MR
460 QTAILQ_FOREACH(mount, &mounts, next) {
461 logged = false;
e3d4d252
MR
462 fd = qemu_open(mount->dirname, O_RDONLY);
463 if (fd == -1) {
e3d4d252
MR
464 continue;
465 }
9e8aded4
MR
466 /* we have no way of knowing whether a filesystem was actually unfrozen
467 * as a result of a successful call to FITHAW, only that if an error
468 * was returned the filesystem was *not* unfrozen by that particular
469 * call.
470 *
a31f0531 471 * since multiple preceding FIFREEZEs require multiple calls to FITHAW
9e8aded4
MR
472 * to unfreeze, continuing issuing FITHAW until an error is returned,
473 * in which case either the filesystem is in an unfreezable state, or,
474 * more likely, it was thawed previously (and remains so afterward).
475 *
476 * also, since the most recent successful call is the one that did
477 * the actual unfreeze, we can use this to provide an accurate count
478 * of the number of filesystems unfrozen by guest-fsfreeze-thaw, which
479 * may * be useful for determining whether a filesystem was unfrozen
480 * during the freeze/thaw phase by a process other than qemu-ga.
481 */
482 do {
483 ret = ioctl(fd, FITHAW);
484 if (ret == 0 && !logged) {
485 i++;
486 logged = true;
487 }
488 } while (ret == 0);
e3d4d252 489 close(fd);
e3d4d252
MR
490 }
491
f22d85e9 492 ga_unset_frozen(ga_state);
9e8aded4 493 guest_fsfreeze_free_mount_list(&mounts);
e3d4d252
MR
494 return i;
495}
496
e3d4d252
MR
497static void guest_fsfreeze_cleanup(void)
498{
499 int64_t ret;
500 Error *err = NULL;
501
f22d85e9 502 if (ga_is_frozen(ga_state) == GUEST_FSFREEZE_STATUS_FROZEN) {
e3d4d252
MR
503 ret = qmp_guest_fsfreeze_thaw(&err);
504 if (ret < 0 || err) {
505 slog("failed to clean up frozen filesystems");
506 }
507 }
508}
e72c3f2e 509#endif /* CONFIG_FSFREEZE */
e3d4d252 510
11d0f125
LC
511#define LINUX_SYS_STATE_FILE "/sys/power/state"
512#define SUSPEND_SUPPORTED 0
513#define SUSPEND_NOT_SUPPORTED 1
514
11d0f125
LC
515static void bios_supports_mode(const char *pmutils_bin, const char *pmutils_arg,
516 const char *sysfile_str, Error **err)
517{
11d0f125 518 char *pmutils_path;
dc8764f0
LC
519 pid_t pid, rpid;
520 int status;
11d0f125
LC
521
522 pmutils_path = g_find_program_in_path(pmutils_bin);
523
524 pid = fork();
525 if (!pid) {
dc8764f0
LC
526 char buf[32]; /* hopefully big enough */
527 ssize_t ret;
528 int fd;
11d0f125
LC
529
530 setsid();
11d0f125
LC
531 reopen_fd_to_null(0);
532 reopen_fd_to_null(1);
533 reopen_fd_to_null(2);
534
dc8764f0
LC
535 if (pmutils_path) {
536 execle(pmutils_path, pmutils_bin, pmutils_arg, NULL, environ);
537 }
11d0f125 538
dc8764f0
LC
539 /*
540 * If we get here either pm-utils is not installed or execle() has
541 * failed. Let's try the manual method if the caller wants it.
542 */
11d0f125 543
dc8764f0
LC
544 if (!sysfile_str) {
545 _exit(SUSPEND_NOT_SUPPORTED);
546 }
11d0f125 547
dc8764f0
LC
548 fd = open(LINUX_SYS_STATE_FILE, O_RDONLY);
549 if (fd < 0) {
11d0f125
LC
550 _exit(SUSPEND_NOT_SUPPORTED);
551 }
552
dc8764f0
LC
553 ret = read(fd, buf, sizeof(buf)-1);
554 if (ret <= 0) {
555 _exit(SUSPEND_NOT_SUPPORTED);
11d0f125 556 }
dc8764f0 557 buf[ret] = '\0';
11d0f125 558
dc8764f0
LC
559 if (strstr(buf, sysfile_str)) {
560 _exit(SUSPEND_SUPPORTED);
11d0f125
LC
561 }
562
dc8764f0 563 _exit(SUSPEND_NOT_SUPPORTED);
11d0f125
LC
564 }
565
11d0f125
LC
566 g_free(pmutils_path);
567
568 if (pid < 0) {
dc8764f0
LC
569 goto undef_err;
570 }
571
572 do {
573 rpid = waitpid(pid, &status, 0);
574 } while (rpid == -1 && errno == EINTR);
575 if (rpid == pid && WIFEXITED(status)) {
576 switch (WEXITSTATUS(status)) {
577 case SUSPEND_SUPPORTED:
578 return;
579 case SUSPEND_NOT_SUPPORTED:
580 error_set(err, QERR_UNSUPPORTED);
581 return;
582 default:
583 goto undef_err;
584 }
11d0f125
LC
585 }
586
dc8764f0
LC
587undef_err:
588 error_set(err, QERR_UNDEFINED_ERROR);
11d0f125
LC
589}
590
591static void guest_suspend(const char *pmutils_bin, const char *sysfile_str,
592 Error **err)
593{
11d0f125 594 char *pmutils_path;
dc8764f0
LC
595 pid_t rpid, pid;
596 int status;
11d0f125
LC
597
598 pmutils_path = g_find_program_in_path(pmutils_bin);
599
600 pid = fork();
601 if (pid == 0) {
602 /* child */
603 int fd;
604
605 setsid();
606 reopen_fd_to_null(0);
607 reopen_fd_to_null(1);
608 reopen_fd_to_null(2);
609
610 if (pmutils_path) {
611 execle(pmutils_path, pmutils_bin, NULL, environ);
612 }
613
614 /*
615 * If we get here either pm-utils is not installed or execle() has
616 * failed. Let's try the manual method if the caller wants it.
617 */
618
619 if (!sysfile_str) {
620 _exit(EXIT_FAILURE);
621 }
622
623 fd = open(LINUX_SYS_STATE_FILE, O_WRONLY);
624 if (fd < 0) {
625 _exit(EXIT_FAILURE);
626 }
627
628 if (write(fd, sysfile_str, strlen(sysfile_str)) < 0) {
629 _exit(EXIT_FAILURE);
630 }
631
632 _exit(EXIT_SUCCESS);
633 }
634
635 g_free(pmutils_path);
636
637 if (pid < 0) {
dc8764f0
LC
638 goto exit_err;
639 }
640
641 do {
642 rpid = waitpid(pid, &status, 0);
643 } while (rpid == -1 && errno == EINTR);
644 if (rpid == pid && WIFEXITED(status) && !WEXITSTATUS(status)) {
11d0f125
LC
645 return;
646 }
dc8764f0
LC
647
648exit_err:
649 error_set(err, QERR_UNDEFINED_ERROR);
11d0f125
LC
650}
651
652void qmp_guest_suspend_disk(Error **err)
653{
654 bios_supports_mode("pm-is-supported", "--hibernate", "disk", err);
655 if (error_is_set(err)) {
656 return;
657 }
658
659 guest_suspend("pm-hibernate", "disk", err);
660}
661
fbf42210
LC
662void qmp_guest_suspend_ram(Error **err)
663{
664 bios_supports_mode("pm-is-supported", "--suspend", "mem", err);
665 if (error_is_set(err)) {
666 return;
667 }
668
669 guest_suspend("pm-suspend", "mem", err);
670}
671
95f4f404
LC
672void qmp_guest_suspend_hybrid(Error **err)
673{
674 bios_supports_mode("pm-is-supported", "--suspend-hybrid", NULL, err);
675 if (error_is_set(err)) {
676 return;
677 }
678
679 guest_suspend("pm-suspend-hybrid", NULL, err);
680}
681
3424fc9f
MP
682static GuestNetworkInterfaceList *
683guest_find_interface(GuestNetworkInterfaceList *head,
684 const char *name)
685{
686 for (; head; head = head->next) {
687 if (strcmp(head->value->name, name) == 0) {
688 break;
689 }
690 }
691
692 return head;
693}
694
695/*
696 * Build information about guest interfaces
697 */
698GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp)
699{
700 GuestNetworkInterfaceList *head = NULL, *cur_item = NULL;
701 struct ifaddrs *ifap, *ifa;
702 char err_msg[512];
703
704 if (getifaddrs(&ifap) < 0) {
705 snprintf(err_msg, sizeof(err_msg),
706 "getifaddrs failed: %s", strerror(errno));
707 error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg);
708 goto error;
709 }
710
711 for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
712 GuestNetworkInterfaceList *info;
713 GuestIpAddressList **address_list = NULL, *address_item = NULL;
714 char addr4[INET_ADDRSTRLEN];
715 char addr6[INET6_ADDRSTRLEN];
716 int sock;
717 struct ifreq ifr;
718 unsigned char *mac_addr;
719 void *p;
720
721 g_debug("Processing %s interface", ifa->ifa_name);
722
723 info = guest_find_interface(head, ifa->ifa_name);
724
725 if (!info) {
726 info = g_malloc0(sizeof(*info));
727 info->value = g_malloc0(sizeof(*info->value));
728 info->value->name = g_strdup(ifa->ifa_name);
729
730 if (!cur_item) {
731 head = cur_item = info;
732 } else {
733 cur_item->next = info;
734 cur_item = info;
735 }
736 }
737
738 if (!info->value->has_hardware_address &&
739 ifa->ifa_flags & SIOCGIFHWADDR) {
740 /* we haven't obtained HW address yet */
741 sock = socket(PF_INET, SOCK_STREAM, 0);
742 if (sock == -1) {
743 snprintf(err_msg, sizeof(err_msg),
744 "failed to create socket: %s", strerror(errno));
745 error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg);
746 goto error;
747 }
748
749 memset(&ifr, 0, sizeof(ifr));
750 strncpy(ifr.ifr_name, info->value->name, IF_NAMESIZE);
751 if (ioctl(sock, SIOCGIFHWADDR, &ifr) == -1) {
752 snprintf(err_msg, sizeof(err_msg),
a31f0531 753 "failed to get MAC address of %s: %s",
3424fc9f
MP
754 ifa->ifa_name,
755 strerror(errno));
756 error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg);
757 goto error;
758 }
759
760 mac_addr = (unsigned char *) &ifr.ifr_hwaddr.sa_data;
761
762 if (asprintf(&info->value->hardware_address,
763 "%02x:%02x:%02x:%02x:%02x:%02x",
764 (int) mac_addr[0], (int) mac_addr[1],
765 (int) mac_addr[2], (int) mac_addr[3],
766 (int) mac_addr[4], (int) mac_addr[5]) == -1) {
767 snprintf(err_msg, sizeof(err_msg),
768 "failed to format MAC: %s", strerror(errno));
769 error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg);
770 goto error;
771 }
772
773 info->value->has_hardware_address = true;
774 close(sock);
775 }
776
777 if (ifa->ifa_addr &&
778 ifa->ifa_addr->sa_family == AF_INET) {
779 /* interface with IPv4 address */
780 address_item = g_malloc0(sizeof(*address_item));
781 address_item->value = g_malloc0(sizeof(*address_item->value));
782 p = &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
783 if (!inet_ntop(AF_INET, p, addr4, sizeof(addr4))) {
784 snprintf(err_msg, sizeof(err_msg),
785 "inet_ntop failed : %s", strerror(errno));
786 error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg);
787 goto error;
788 }
789
790 address_item->value->ip_address = g_strdup(addr4);
791 address_item->value->ip_address_type = GUEST_IP_ADDRESS_TYPE_IPV4;
792
793 if (ifa->ifa_netmask) {
794 /* Count the number of set bits in netmask.
795 * This is safe as '1' and '0' cannot be shuffled in netmask. */
796 p = &((struct sockaddr_in *)ifa->ifa_netmask)->sin_addr;
797 address_item->value->prefix = ctpop32(((uint32_t *) p)[0]);
798 }
799 } else if (ifa->ifa_addr &&
800 ifa->ifa_addr->sa_family == AF_INET6) {
801 /* interface with IPv6 address */
802 address_item = g_malloc0(sizeof(*address_item));
803 address_item->value = g_malloc0(sizeof(*address_item->value));
804 p = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
805 if (!inet_ntop(AF_INET6, p, addr6, sizeof(addr6))) {
806 snprintf(err_msg, sizeof(err_msg),
807 "inet_ntop failed : %s", strerror(errno));
808 error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg);
809 goto error;
810 }
811
812 address_item->value->ip_address = g_strdup(addr6);
813 address_item->value->ip_address_type = GUEST_IP_ADDRESS_TYPE_IPV6;
814
815 if (ifa->ifa_netmask) {
816 /* Count the number of set bits in netmask.
817 * This is safe as '1' and '0' cannot be shuffled in netmask. */
818 p = &((struct sockaddr_in6 *)ifa->ifa_netmask)->sin6_addr;
819 address_item->value->prefix =
820 ctpop32(((uint32_t *) p)[0]) +
821 ctpop32(((uint32_t *) p)[1]) +
822 ctpop32(((uint32_t *) p)[2]) +
823 ctpop32(((uint32_t *) p)[3]);
824 }
825 }
826
827 if (!address_item) {
828 continue;
829 }
830
831 address_list = &info->value->ip_addresses;
832
833 while (*address_list && (*address_list)->next) {
834 address_list = &(*address_list)->next;
835 }
836
837 if (!*address_list) {
838 *address_list = address_item;
839 } else {
840 (*address_list)->next = address_item;
841 }
842
843 info->value->has_ip_addresses = true;
844
845
846 }
847
848 freeifaddrs(ifap);
849 return head;
850
851error:
852 freeifaddrs(ifap);
853 qapi_free_GuestNetworkInterfaceList(head);
854 return NULL;
855}
856
e72c3f2e
MR
857#else /* defined(__linux__) */
858
d35d4cb5 859void qmp_guest_suspend_disk(Error **err)
e72c3f2e
MR
860{
861 error_set(err, QERR_UNSUPPORTED);
e72c3f2e
MR
862}
863
d35d4cb5 864void qmp_guest_suspend_ram(Error **err)
e72c3f2e
MR
865{
866 error_set(err, QERR_UNSUPPORTED);
e72c3f2e
MR
867}
868
d35d4cb5 869void qmp_guest_suspend_hybrid(Error **err)
e72c3f2e
MR
870{
871 error_set(err, QERR_UNSUPPORTED);
e72c3f2e
MR
872}
873
d35d4cb5 874GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp)
e72c3f2e 875{
d35d4cb5
MR
876 error_set(errp, QERR_UNSUPPORTED);
877 return NULL;
e72c3f2e
MR
878}
879
d35d4cb5
MR
880#endif
881
882#if !defined(CONFIG_FSFREEZE)
883
884GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **err)
e72c3f2e
MR
885{
886 error_set(err, QERR_UNSUPPORTED);
d35d4cb5
MR
887
888 return 0;
e72c3f2e
MR
889}
890
d35d4cb5 891int64_t qmp_guest_fsfreeze_freeze(Error **err)
e72c3f2e
MR
892{
893 error_set(err, QERR_UNSUPPORTED);
d35d4cb5
MR
894
895 return 0;
e72c3f2e
MR
896}
897
d35d4cb5 898int64_t qmp_guest_fsfreeze_thaw(Error **err)
e72c3f2e 899{
d35d4cb5
MR
900 error_set(err, QERR_UNSUPPORTED);
901
902 return 0;
e72c3f2e
MR
903}
904
905#endif
906
e3d4d252
MR
907/* register init/cleanup routines for stateful command groups */
908void ga_command_state_init(GAState *s, GACommandState *cs)
909{
7006b9cf 910#if defined(CONFIG_FSFREEZE)
f22d85e9 911 ga_command_state_add(cs, NULL, guest_fsfreeze_cleanup);
7006b9cf 912#endif
e3d4d252
MR
913 ga_command_state_add(cs, guest_file_init, NULL);
914}