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