Statistics
| Branch: | Revision:

root / qga / commands-posix.c @ 9e8aded4

History | View | Annotate | Download (25.3 kB)

1
/*
2
 * QEMU Guest Agent POSIX-specific command implementations
3
 *
4
 * Copyright IBM Corp. 2011
5
 *
6
 * Authors:
7
 *  Michael Roth      <mdroth@linux.vnet.ibm.com>
8
 *  Michal Privoznik  <mprivozn@redhat.com>
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>
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"
22

    
23
#if defined(__linux__)
24
#include <mntent.h>
25
#include <linux/fs.h>
26
#include <ifaddrs.h>
27
#include <arpa/inet.h>
28
#include <sys/socket.h>
29
#include <net/if.h>
30
#include <sys/wait.h>
31

    
32
#if defined(__linux__) && defined(FIFREEZE)
33
#define CONFIG_FSFREEZE
34
#endif
35
#endif
36

    
37
#if defined(__linux__)
38
/* TODO: use this in place of all post-fork() fclose(std*) callers */
39
static void reopen_fd_to_null(int fd)
40
{
41
    int nullfd;
42

    
43
    nullfd = open("/dev/null", O_RDWR);
44
    if (nullfd < 0) {
45
        return;
46
    }
47

    
48
    dup2(nullfd, fd);
49

    
50
    if (nullfd != fd) {
51
        close(nullfd);
52
    }
53
}
54
#endif /* defined(__linux__) */
55

    
56
void qmp_guest_shutdown(bool has_mode, const char *mode, Error **err)
57
{
58
    int ret;
59
    const char *shutdown_flag;
60

    
61
    slog("guest-shutdown called, mode: %s", mode);
62
    if (!has_mode || strcmp(mode, "powerdown") == 0) {
63
        shutdown_flag = "-P";
64
    } else if (strcmp(mode, "halt") == 0) {
65
        shutdown_flag = "-H";
66
    } else if (strcmp(mode, "reboot") == 0) {
67
        shutdown_flag = "-r";
68
    } else {
69
        error_set(err, QERR_INVALID_PARAMETER_VALUE, "mode",
70
                  "halt|powerdown|reboot");
71
        return;
72
    }
73

    
74
    ret = fork();
75
    if (ret == 0) {
76
        /* child, start the shutdown */
77
        setsid();
78
        fclose(stdin);
79
        fclose(stdout);
80
        fclose(stderr);
81

    
82
        ret = execl("/sbin/shutdown", "shutdown", shutdown_flag, "+0",
83
                    "hypervisor initiated shutdown", (char*)NULL);
84
        if (ret) {
85
            slog("guest-shutdown failed: %s", strerror(errno));
86
        }
87
        exit(!!ret);
88
    } else if (ret < 0) {
89
        error_set(err, QERR_UNDEFINED_ERROR);
90
    }
91
}
92

    
93
typedef struct GuestFileHandle {
94
    uint64_t id;
95
    FILE *fh;
96
    QTAILQ_ENTRY(GuestFileHandle) next;
97
} GuestFileHandle;
98

    
99
static struct {
100
    QTAILQ_HEAD(, GuestFileHandle) filehandles;
101
} guest_file_state;
102

    
103
static void guest_file_handle_add(FILE *fh)
104
{
105
    GuestFileHandle *gfh;
106

    
107
    gfh = g_malloc0(sizeof(GuestFileHandle));
108
    gfh->id = fileno(fh);
109
    gfh->fh = fh;
110
    QTAILQ_INSERT_TAIL(&guest_file_state.filehandles, gfh, next);
111
}
112

    
113
static GuestFileHandle *guest_file_handle_find(int64_t id)
114
{
115
    GuestFileHandle *gfh;
116

    
117
    QTAILQ_FOREACH(gfh, &guest_file_state.filehandles, next)
118
    {
119
        if (gfh->id == id) {
120
            return gfh;
121
        }
122
    }
123

    
124
    return NULL;
125
}
126

    
127
int64_t qmp_guest_file_open(const char *path, bool has_mode, const char *mode, Error **err)
128
{
129
    FILE *fh;
130
    int fd;
131
    int64_t ret = -1;
132

    
133
    if (!has_mode) {
134
        mode = "r";
135
    }
136
    slog("guest-file-open called, filepath: %s, mode: %s", path, mode);
137
    fh = fopen(path, mode);
138
    if (!fh) {
139
        error_set(err, QERR_OPEN_FILE_FAILED, path);
140
        return -1;
141
    }
142

    
143
    /* set fd non-blocking to avoid common use cases (like reading from a
144
     * named pipe) from hanging the agent
145
     */
146
    fd = fileno(fh);
147
    ret = fcntl(fd, F_GETFL);
148
    ret = fcntl(fd, F_SETFL, ret | O_NONBLOCK);
149
    if (ret == -1) {
150
        error_set(err, QERR_QGA_COMMAND_FAILED, "fcntl() failed");
151
        fclose(fh);
152
        return -1;
153
    }
154

    
155
    guest_file_handle_add(fh);
156
    slog("guest-file-open, handle: %d", fd);
157
    return fd;
158
}
159

    
160
void qmp_guest_file_close(int64_t handle, Error **err)
161
{
162
    GuestFileHandle *gfh = guest_file_handle_find(handle);
163
    int ret;
164

    
165
    slog("guest-file-close called, handle: %ld", handle);
166
    if (!gfh) {
167
        error_set(err, QERR_FD_NOT_FOUND, "handle");
168
        return;
169
    }
170

    
171
    ret = fclose(gfh->fh);
172
    if (ret == -1) {
173
        error_set(err, QERR_QGA_COMMAND_FAILED, "fclose() failed");
174
        return;
175
    }
176

    
177
    QTAILQ_REMOVE(&guest_file_state.filehandles, gfh, next);
178
    g_free(gfh);
179
}
180

    
181
struct GuestFileRead *qmp_guest_file_read(int64_t handle, bool has_count,
182
                                          int64_t count, Error **err)
183
{
184
    GuestFileHandle *gfh = guest_file_handle_find(handle);
185
    GuestFileRead *read_data = NULL;
186
    guchar *buf;
187
    FILE *fh;
188
    size_t read_count;
189

    
190
    if (!gfh) {
191
        error_set(err, QERR_FD_NOT_FOUND, "handle");
192
        return NULL;
193
    }
194

    
195
    if (!has_count) {
196
        count = QGA_READ_COUNT_DEFAULT;
197
    } else if (count < 0) {
198
        error_set(err, QERR_INVALID_PARAMETER, "count");
199
        return NULL;
200
    }
201

    
202
    fh = gfh->fh;
203
    buf = g_malloc0(count+1);
204
    read_count = fread(buf, 1, count, fh);
205
    if (ferror(fh)) {
206
        slog("guest-file-read failed, handle: %ld", handle);
207
        error_set(err, QERR_QGA_COMMAND_FAILED, "fread() failed");
208
    } else {
209
        buf[read_count] = 0;
210
        read_data = g_malloc0(sizeof(GuestFileRead));
211
        read_data->count = read_count;
212
        read_data->eof = feof(fh);
213
        if (read_count) {
214
            read_data->buf_b64 = g_base64_encode(buf, read_count);
215
        }
216
    }
217
    g_free(buf);
218
    clearerr(fh);
219

    
220
    return read_data;
221
}
222

    
223
GuestFileWrite *qmp_guest_file_write(int64_t handle, const char *buf_b64,
224
                                     bool has_count, int64_t count, Error **err)
225
{
226
    GuestFileWrite *write_data = NULL;
227
    guchar *buf;
228
    gsize buf_len;
229
    int write_count;
230
    GuestFileHandle *gfh = guest_file_handle_find(handle);
231
    FILE *fh;
232

    
233
    if (!gfh) {
234
        error_set(err, QERR_FD_NOT_FOUND, "handle");
235
        return NULL;
236
    }
237

    
238
    fh = gfh->fh;
239
    buf = g_base64_decode(buf_b64, &buf_len);
240

    
241
    if (!has_count) {
242
        count = buf_len;
243
    } else if (count < 0 || count > buf_len) {
244
        g_free(buf);
245
        error_set(err, QERR_INVALID_PARAMETER, "count");
246
        return NULL;
247
    }
248

    
249
    write_count = fwrite(buf, 1, count, fh);
250
    if (ferror(fh)) {
251
        slog("guest-file-write failed, handle: %ld", handle);
252
        error_set(err, QERR_QGA_COMMAND_FAILED, "fwrite() error");
253
    } else {
254
        write_data = g_malloc0(sizeof(GuestFileWrite));
255
        write_data->count = write_count;
256
        write_data->eof = feof(fh);
257
    }
258
    g_free(buf);
259
    clearerr(fh);
260

    
261
    return write_data;
262
}
263

    
264
struct GuestFileSeek *qmp_guest_file_seek(int64_t handle, int64_t offset,
265
                                          int64_t whence, Error **err)
266
{
267
    GuestFileHandle *gfh = guest_file_handle_find(handle);
268
    GuestFileSeek *seek_data = NULL;
269
    FILE *fh;
270
    int ret;
271

    
272
    if (!gfh) {
273
        error_set(err, QERR_FD_NOT_FOUND, "handle");
274
        return NULL;
275
    }
276

    
277
    fh = gfh->fh;
278
    ret = fseek(fh, offset, whence);
279
    if (ret == -1) {
280
        error_set(err, QERR_QGA_COMMAND_FAILED, strerror(errno));
281
    } else {
282
        seek_data = g_malloc0(sizeof(GuestFileRead));
283
        seek_data->position = ftell(fh);
284
        seek_data->eof = feof(fh);
285
    }
286
    clearerr(fh);
287

    
288
    return seek_data;
289
}
290

    
291
void qmp_guest_file_flush(int64_t handle, Error **err)
292
{
293
    GuestFileHandle *gfh = guest_file_handle_find(handle);
294
    FILE *fh;
295
    int ret;
296

    
297
    if (!gfh) {
298
        error_set(err, QERR_FD_NOT_FOUND, "handle");
299
        return;
300
    }
301

    
302
    fh = gfh->fh;
303
    ret = fflush(fh);
304
    if (ret == EOF) {
305
        error_set(err, QERR_QGA_COMMAND_FAILED, strerror(errno));
306
    }
307
}
308

    
309
static void guest_file_init(void)
310
{
311
    QTAILQ_INIT(&guest_file_state.filehandles);
312
}
313

    
314
/* linux-specific implementations. avoid this if at all possible. */
315
#if defined(__linux__)
316

    
317
#if defined(CONFIG_FSFREEZE)
318

    
319
static void disable_logging(void)
320
{
321
    ga_disable_logging(ga_state);
322
}
323

    
324
static void enable_logging(void)
325
{
326
    ga_enable_logging(ga_state);
327
}
328

    
329
typedef struct GuestFsfreezeMount {
330
    char *dirname;
331
    char *devtype;
332
    QTAILQ_ENTRY(GuestFsfreezeMount) next;
333
} GuestFsfreezeMount;
334

    
335
typedef QTAILQ_HEAD(, GuestFsfreezeMount) GuestFsfreezeMountList;
336

    
337
struct {
338
    GuestFsfreezeStatus status;
339
} guest_fsfreeze_state;
340

    
341
static void guest_fsfreeze_free_mount_list(GuestFsfreezeMountList *mounts)
342
{
343
     GuestFsfreezeMount *mount, *temp;
344

    
345
     if (!mounts) {
346
         return;
347
     }
348

    
349
     QTAILQ_FOREACH_SAFE(mount, mounts, next, temp) {
350
         QTAILQ_REMOVE(mounts, mount, next);
351
         g_free(mount->dirname);
352
         g_free(mount->devtype);
353
         g_free(mount);
354
     }
355
}
356

    
357
/*
358
 * Walk the mount table and build a list of local file systems
359
 */
360
static int guest_fsfreeze_build_mount_list(GuestFsfreezeMountList *mounts)
361
{
362
    struct mntent *ment;
363
    GuestFsfreezeMount *mount;
364
    char const *mtab = MOUNTED;
365
    FILE *fp;
366

    
367
    fp = setmntent(mtab, "r");
368
    if (!fp) {
369
        g_warning("fsfreeze: unable to read mtab");
370
        return -1;
371
    }
372

    
373
    while ((ment = getmntent(fp))) {
374
        /*
375
         * An entry which device name doesn't start with a '/' is
376
         * either a dummy file system or a network file system.
377
         * Add special handling for smbfs and cifs as is done by
378
         * coreutils as well.
379
         */
380
        if ((ment->mnt_fsname[0] != '/') ||
381
            (strcmp(ment->mnt_type, "smbfs") == 0) ||
382
            (strcmp(ment->mnt_type, "cifs") == 0)) {
383
            continue;
384
        }
385

    
386
        mount = g_malloc0(sizeof(GuestFsfreezeMount));
387
        mount->dirname = g_strdup(ment->mnt_dir);
388
        mount->devtype = g_strdup(ment->mnt_type);
389

    
390
        QTAILQ_INSERT_TAIL(mounts, mount, next);
391
    }
392

    
393
    endmntent(fp);
394

    
395
    return 0;
396
}
397

    
398
/*
399
 * Return status of freeze/thaw
400
 */
401
GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **err)
402
{
403
    return guest_fsfreeze_state.status;
404
}
405

    
406
/*
407
 * Walk list of mounted file systems in the guest, and freeze the ones which
408
 * are real local file systems.
409
 */
410
int64_t qmp_guest_fsfreeze_freeze(Error **err)
411
{
412
    int ret = 0, i = 0;
413
    GuestFsfreezeMountList mounts;
414
    struct GuestFsfreezeMount *mount;
415
    int fd;
416
    char err_msg[512];
417

    
418
    slog("guest-fsfreeze called");
419

    
420
    QTAILQ_INIT(&mounts);
421
    ret = guest_fsfreeze_build_mount_list(&mounts);
422
    if (ret < 0) {
423
        return ret;
424
    }
425

    
426
    /* cannot risk guest agent blocking itself on a write in this state */
427
    disable_logging();
428

    
429
    QTAILQ_FOREACH(mount, &mounts, next) {
430
        fd = qemu_open(mount->dirname, O_RDONLY);
431
        if (fd == -1) {
432
            sprintf(err_msg, "failed to open %s, %s", mount->dirname,
433
                    strerror(errno));
434
            error_set(err, QERR_QGA_COMMAND_FAILED, err_msg);
435
            goto error;
436
        }
437

    
438
        /* we try to cull filesytems we know won't work in advance, but other
439
         * filesytems may not implement fsfreeze for less obvious reasons.
440
         * these will report EOPNOTSUPP. we simply ignore these when tallying
441
         * the number of frozen filesystems.
442
         *
443
         * any other error means a failure to freeze a filesystem we
444
         * expect to be freezable, so return an error in those cases
445
         * and return system to thawed state.
446
         */
447
        ret = ioctl(fd, FIFREEZE);
448
        if (ret == -1) {
449
            if (errno != EOPNOTSUPP) {
450
                sprintf(err_msg, "failed to freeze %s, %s",
451
                        mount->dirname, strerror(errno));
452
                error_set(err, QERR_QGA_COMMAND_FAILED, err_msg);
453
                close(fd);
454
                goto error;
455
            }
456
        } else {
457
            i++;
458
        }
459
        close(fd);
460
    }
461

    
462
    guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_FROZEN;
463
    guest_fsfreeze_free_mount_list(&mounts);
464
    return i;
465

    
466
error:
467
    guest_fsfreeze_free_mount_list(&mounts);
468
    qmp_guest_fsfreeze_thaw(NULL);
469
    return 0;
470
}
471

    
472
/*
473
 * Walk list of frozen file systems in the guest, and thaw them.
474
 */
475
int64_t qmp_guest_fsfreeze_thaw(Error **err)
476
{
477
    int ret;
478
    GuestFsfreezeMountList mounts;
479
    GuestFsfreezeMount *mount;
480
    int fd, i = 0, logged;
481

    
482
    QTAILQ_INIT(&mounts);
483
    ret = guest_fsfreeze_build_mount_list(&mounts);
484
    if (ret) {
485
        error_set(err, QERR_QGA_COMMAND_FAILED,
486
                  "failed to enumerate filesystems");
487
        return 0;
488
    }
489

    
490
    QTAILQ_FOREACH(mount, &mounts, next) {
491
        logged = false;
492
        fd = qemu_open(mount->dirname, O_RDONLY);
493
        if (fd == -1) {
494
            continue;
495
        }
496
        /* we have no way of knowing whether a filesystem was actually unfrozen
497
         * as a result of a successful call to FITHAW, only that if an error
498
         * was returned the filesystem was *not* unfrozen by that particular
499
         * call.
500
         *
501
         * since multiple preceeding FIFREEZEs require multiple calls to FITHAW
502
         * to unfreeze, continuing issuing FITHAW until an error is returned,
503
         * in which case either the filesystem is in an unfreezable state, or,
504
         * more likely, it was thawed previously (and remains so afterward).
505
         *
506
         * also, since the most recent successful call is the one that did
507
         * the actual unfreeze, we can use this to provide an accurate count
508
         * of the number of filesystems unfrozen by guest-fsfreeze-thaw, which
509
         * may * be useful for determining whether a filesystem was unfrozen
510
         * during the freeze/thaw phase by a process other than qemu-ga.
511
         */
512
        do {
513
            ret = ioctl(fd, FITHAW);
514
            if (ret == 0 && !logged) {
515
                i++;
516
                logged = true;
517
            }
518
        } while (ret == 0);
519
        close(fd);
520
    }
521

    
522
    guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_THAWED;
523
    enable_logging();
524
    guest_fsfreeze_free_mount_list(&mounts);
525
    return i;
526
}
527

    
528
static void guest_fsfreeze_init(void)
529
{
530
    guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_THAWED;
531
}
532

    
533
static void guest_fsfreeze_cleanup(void)
534
{
535
    int64_t ret;
536
    Error *err = NULL;
537

    
538
    if (guest_fsfreeze_state.status == GUEST_FSFREEZE_STATUS_FROZEN) {
539
        ret = qmp_guest_fsfreeze_thaw(&err);
540
        if (ret < 0 || err) {
541
            slog("failed to clean up frozen filesystems");
542
        }
543
    }
544
}
545
#endif /* CONFIG_FSFREEZE */
546

    
547
#define LINUX_SYS_STATE_FILE "/sys/power/state"
548
#define SUSPEND_SUPPORTED 0
549
#define SUSPEND_NOT_SUPPORTED 1
550

    
551
/**
552
 * This function forks twice and the information about the mode support
553
 * status is passed to the qemu-ga process via a pipe.
554
 *
555
 * This approach allows us to keep the way we reap terminated children
556
 * in qemu-ga quite simple.
557
 */
558
static void bios_supports_mode(const char *pmutils_bin, const char *pmutils_arg,
559
                               const char *sysfile_str, Error **err)
560
{
561
    pid_t pid;
562
    ssize_t ret;
563
    char *pmutils_path;
564
    int status, pipefds[2];
565

    
566
    if (pipe(pipefds) < 0) {
567
        error_set(err, QERR_UNDEFINED_ERROR);
568
        return;
569
    }
570

    
571
    pmutils_path = g_find_program_in_path(pmutils_bin);
572

    
573
    pid = fork();
574
    if (!pid) {
575
        struct sigaction act;
576

    
577
        memset(&act, 0, sizeof(act));
578
        act.sa_handler = SIG_DFL;
579
        sigaction(SIGCHLD, &act, NULL);
580

    
581
        setsid();
582
        close(pipefds[0]);
583
        reopen_fd_to_null(0);
584
        reopen_fd_to_null(1);
585
        reopen_fd_to_null(2);
586

    
587
        pid = fork();
588
        if (!pid) {
589
            int fd;
590
            char buf[32]; /* hopefully big enough */
591

    
592
            if (pmutils_path) {
593
                execle(pmutils_path, pmutils_bin, pmutils_arg, NULL, environ);
594
            }
595

    
596
            /*
597
             * If we get here either pm-utils is not installed or execle() has
598
             * failed. Let's try the manual method if the caller wants it.
599
             */
600

    
601
            if (!sysfile_str) {
602
                _exit(SUSPEND_NOT_SUPPORTED);
603
            }
604

    
605
            fd = open(LINUX_SYS_STATE_FILE, O_RDONLY);
606
            if (fd < 0) {
607
                _exit(SUSPEND_NOT_SUPPORTED);
608
            }
609

    
610
            ret = read(fd, buf, sizeof(buf)-1);
611
            if (ret <= 0) {
612
                _exit(SUSPEND_NOT_SUPPORTED);
613
            }
614
            buf[ret] = '\0';
615

    
616
            if (strstr(buf, sysfile_str)) {
617
                _exit(SUSPEND_SUPPORTED);
618
            }
619

    
620
            _exit(SUSPEND_NOT_SUPPORTED);
621
        }
622

    
623
        if (pid > 0) {
624
            wait(&status);
625
        } else {
626
            status = SUSPEND_NOT_SUPPORTED;
627
        }
628

    
629
        ret = write(pipefds[1], &status, sizeof(status));
630
        if (ret != sizeof(status)) {
631
            _exit(EXIT_FAILURE);
632
        }
633

    
634
        _exit(EXIT_SUCCESS);
635
    }
636

    
637
    close(pipefds[1]);
638
    g_free(pmutils_path);
639

    
640
    if (pid < 0) {
641
        error_set(err, QERR_UNDEFINED_ERROR);
642
        goto out;
643
    }
644

    
645
    ret = read(pipefds[0], &status, sizeof(status));
646
    if (ret == sizeof(status) && WIFEXITED(status) &&
647
        WEXITSTATUS(status) == SUSPEND_SUPPORTED) {
648
            goto out;
649
    }
650

    
651
    error_set(err, QERR_UNSUPPORTED);
652

    
653
out:
654
    close(pipefds[0]);
655
}
656

    
657
static void guest_suspend(const char *pmutils_bin, const char *sysfile_str,
658
                          Error **err)
659
{
660
    pid_t pid;
661
    char *pmutils_path;
662

    
663
    pmutils_path = g_find_program_in_path(pmutils_bin);
664

    
665
    pid = fork();
666
    if (pid == 0) {
667
        /* child */
668
        int fd;
669

    
670
        setsid();
671
        reopen_fd_to_null(0);
672
        reopen_fd_to_null(1);
673
        reopen_fd_to_null(2);
674

    
675
        if (pmutils_path) {
676
            execle(pmutils_path, pmutils_bin, NULL, environ);
677
        }
678

    
679
        /*
680
         * If we get here either pm-utils is not installed or execle() has
681
         * failed. Let's try the manual method if the caller wants it.
682
         */
683

    
684
        if (!sysfile_str) {
685
            _exit(EXIT_FAILURE);
686
        }
687

    
688
        fd = open(LINUX_SYS_STATE_FILE, O_WRONLY);
689
        if (fd < 0) {
690
            _exit(EXIT_FAILURE);
691
        }
692

    
693
        if (write(fd, sysfile_str, strlen(sysfile_str)) < 0) {
694
            _exit(EXIT_FAILURE);
695
        }
696

    
697
        _exit(EXIT_SUCCESS);
698
    }
699

    
700
    g_free(pmutils_path);
701

    
702
    if (pid < 0) {
703
        error_set(err, QERR_UNDEFINED_ERROR);
704
        return;
705
    }
706
}
707

    
708
void qmp_guest_suspend_disk(Error **err)
709
{
710
    bios_supports_mode("pm-is-supported", "--hibernate", "disk", err);
711
    if (error_is_set(err)) {
712
        return;
713
    }
714

    
715
    guest_suspend("pm-hibernate", "disk", err);
716
}
717

    
718
void qmp_guest_suspend_ram(Error **err)
719
{
720
    bios_supports_mode("pm-is-supported", "--suspend", "mem", err);
721
    if (error_is_set(err)) {
722
        return;
723
    }
724

    
725
    guest_suspend("pm-suspend", "mem", err);
726
}
727

    
728
void qmp_guest_suspend_hybrid(Error **err)
729
{
730
    bios_supports_mode("pm-is-supported", "--suspend-hybrid", NULL, err);
731
    if (error_is_set(err)) {
732
        return;
733
    }
734

    
735
    guest_suspend("pm-suspend-hybrid", NULL, err);
736
}
737

    
738
static GuestNetworkInterfaceList *
739
guest_find_interface(GuestNetworkInterfaceList *head,
740
                     const char *name)
741
{
742
    for (; head; head = head->next) {
743
        if (strcmp(head->value->name, name) == 0) {
744
            break;
745
        }
746
    }
747

    
748
    return head;
749
}
750

    
751
/*
752
 * Build information about guest interfaces
753
 */
754
GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp)
755
{
756
    GuestNetworkInterfaceList *head = NULL, *cur_item = NULL;
757
    struct ifaddrs *ifap, *ifa;
758
    char err_msg[512];
759

    
760
    if (getifaddrs(&ifap) < 0) {
761
        snprintf(err_msg, sizeof(err_msg),
762
                 "getifaddrs failed: %s", strerror(errno));
763
        error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg);
764
        goto error;
765
    }
766

    
767
    for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
768
        GuestNetworkInterfaceList *info;
769
        GuestIpAddressList **address_list = NULL, *address_item = NULL;
770
        char addr4[INET_ADDRSTRLEN];
771
        char addr6[INET6_ADDRSTRLEN];
772
        int sock;
773
        struct ifreq ifr;
774
        unsigned char *mac_addr;
775
        void *p;
776

    
777
        g_debug("Processing %s interface", ifa->ifa_name);
778

    
779
        info = guest_find_interface(head, ifa->ifa_name);
780

    
781
        if (!info) {
782
            info = g_malloc0(sizeof(*info));
783
            info->value = g_malloc0(sizeof(*info->value));
784
            info->value->name = g_strdup(ifa->ifa_name);
785

    
786
            if (!cur_item) {
787
                head = cur_item = info;
788
            } else {
789
                cur_item->next = info;
790
                cur_item = info;
791
            }
792
        }
793

    
794
        if (!info->value->has_hardware_address &&
795
            ifa->ifa_flags & SIOCGIFHWADDR) {
796
            /* we haven't obtained HW address yet */
797
            sock = socket(PF_INET, SOCK_STREAM, 0);
798
            if (sock == -1) {
799
                snprintf(err_msg, sizeof(err_msg),
800
                         "failed to create socket: %s", strerror(errno));
801
                error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg);
802
                goto error;
803
            }
804

    
805
            memset(&ifr, 0, sizeof(ifr));
806
            strncpy(ifr.ifr_name,  info->value->name, IF_NAMESIZE);
807
            if (ioctl(sock, SIOCGIFHWADDR, &ifr) == -1) {
808
                snprintf(err_msg, sizeof(err_msg),
809
                         "failed to get MAC addres of %s: %s",
810
                         ifa->ifa_name,
811
                         strerror(errno));
812
                error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg);
813
                goto error;
814
            }
815

    
816
            mac_addr = (unsigned char *) &ifr.ifr_hwaddr.sa_data;
817

    
818
            if (asprintf(&info->value->hardware_address,
819
                         "%02x:%02x:%02x:%02x:%02x:%02x",
820
                         (int) mac_addr[0], (int) mac_addr[1],
821
                         (int) mac_addr[2], (int) mac_addr[3],
822
                         (int) mac_addr[4], (int) mac_addr[5]) == -1) {
823
                snprintf(err_msg, sizeof(err_msg),
824
                         "failed to format MAC: %s", strerror(errno));
825
                error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg);
826
                goto error;
827
            }
828

    
829
            info->value->has_hardware_address = true;
830
            close(sock);
831
        }
832

    
833
        if (ifa->ifa_addr &&
834
            ifa->ifa_addr->sa_family == AF_INET) {
835
            /* interface with IPv4 address */
836
            address_item = g_malloc0(sizeof(*address_item));
837
            address_item->value = g_malloc0(sizeof(*address_item->value));
838
            p = &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
839
            if (!inet_ntop(AF_INET, p, addr4, sizeof(addr4))) {
840
                snprintf(err_msg, sizeof(err_msg),
841
                         "inet_ntop failed : %s", strerror(errno));
842
                error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg);
843
                goto error;
844
            }
845

    
846
            address_item->value->ip_address = g_strdup(addr4);
847
            address_item->value->ip_address_type = GUEST_IP_ADDRESS_TYPE_IPV4;
848

    
849
            if (ifa->ifa_netmask) {
850
                /* Count the number of set bits in netmask.
851
                 * This is safe as '1' and '0' cannot be shuffled in netmask. */
852
                p = &((struct sockaddr_in *)ifa->ifa_netmask)->sin_addr;
853
                address_item->value->prefix = ctpop32(((uint32_t *) p)[0]);
854
            }
855
        } else if (ifa->ifa_addr &&
856
                   ifa->ifa_addr->sa_family == AF_INET6) {
857
            /* interface with IPv6 address */
858
            address_item = g_malloc0(sizeof(*address_item));
859
            address_item->value = g_malloc0(sizeof(*address_item->value));
860
            p = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
861
            if (!inet_ntop(AF_INET6, p, addr6, sizeof(addr6))) {
862
                snprintf(err_msg, sizeof(err_msg),
863
                         "inet_ntop failed : %s", strerror(errno));
864
                error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg);
865
                goto error;
866
            }
867

    
868
            address_item->value->ip_address = g_strdup(addr6);
869
            address_item->value->ip_address_type = GUEST_IP_ADDRESS_TYPE_IPV6;
870

    
871
            if (ifa->ifa_netmask) {
872
                /* Count the number of set bits in netmask.
873
                 * This is safe as '1' and '0' cannot be shuffled in netmask. */
874
                p = &((struct sockaddr_in6 *)ifa->ifa_netmask)->sin6_addr;
875
                address_item->value->prefix =
876
                    ctpop32(((uint32_t *) p)[0]) +
877
                    ctpop32(((uint32_t *) p)[1]) +
878
                    ctpop32(((uint32_t *) p)[2]) +
879
                    ctpop32(((uint32_t *) p)[3]);
880
            }
881
        }
882

    
883
        if (!address_item) {
884
            continue;
885
        }
886

    
887
        address_list = &info->value->ip_addresses;
888

    
889
        while (*address_list && (*address_list)->next) {
890
            address_list = &(*address_list)->next;
891
        }
892

    
893
        if (!*address_list) {
894
            *address_list = address_item;
895
        } else {
896
            (*address_list)->next = address_item;
897
        }
898

    
899
        info->value->has_ip_addresses = true;
900

    
901

    
902
    }
903

    
904
    freeifaddrs(ifap);
905
    return head;
906

    
907
error:
908
    freeifaddrs(ifap);
909
    qapi_free_GuestNetworkInterfaceList(head);
910
    return NULL;
911
}
912

    
913
#else /* defined(__linux__) */
914

    
915
void qmp_guest_suspend_disk(Error **err)
916
{
917
    error_set(err, QERR_UNSUPPORTED);
918
}
919

    
920
void qmp_guest_suspend_ram(Error **err)
921
{
922
    error_set(err, QERR_UNSUPPORTED);
923
}
924

    
925
void qmp_guest_suspend_hybrid(Error **err)
926
{
927
    error_set(err, QERR_UNSUPPORTED);
928
}
929

    
930
GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp)
931
{
932
    error_set(errp, QERR_UNSUPPORTED);
933
    return NULL;
934
}
935

    
936
#endif
937

    
938
#if !defined(CONFIG_FSFREEZE)
939

    
940
GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **err)
941
{
942
    error_set(err, QERR_UNSUPPORTED);
943

    
944
    return 0;
945
}
946

    
947
int64_t qmp_guest_fsfreeze_freeze(Error **err)
948
{
949
    error_set(err, QERR_UNSUPPORTED);
950

    
951
    return 0;
952
}
953

    
954
int64_t qmp_guest_fsfreeze_thaw(Error **err)
955
{
956
    error_set(err, QERR_UNSUPPORTED);
957

    
958
    return 0;
959
}
960

    
961
#endif
962

    
963
/* register init/cleanup routines for stateful command groups */
964
void ga_command_state_init(GAState *s, GACommandState *cs)
965
{
966
#if defined(CONFIG_FSFREEZE)
967
    ga_command_state_add(cs, guest_fsfreeze_init, guest_fsfreeze_cleanup);
968
#endif
969
    ga_command_state_add(cs, guest_file_init, NULL);
970
}