qemu/migration/socket.c
<<
>>
Prefs
   1/*
   2 * QEMU live migration via socket
   3 *
   4 * Copyright Red Hat, Inc. 2009-2016
   5 *
   6 * Authors:
   7 *  Chris Lalancette <clalance@redhat.com>
   8 *  Daniel P. Berrange <berrange@redhat.com>
   9 *
  10 * This work is licensed under the terms of the GNU GPL, version 2.  See
  11 * the COPYING file in the top-level directory.
  12 *
  13 * Contributions after 2012-01-13 are licensed under the terms of the
  14 * GNU GPL, version 2 or (at your option) any later version.
  15 */
  16
  17#include "qemu/osdep.h"
  18
  19#include "qemu-common.h"
  20#include "qemu/error-report.h"
  21#include "qapi/error.h"
  22#include "channel.h"
  23#include "socket.h"
  24#include "migration.h"
  25#include "qemu-file.h"
  26#include "io/channel-socket.h"
  27#include "trace.h"
  28
  29
  30static SocketAddress *tcp_build_address(const char *host_port, Error **errp)
  31{
  32    SocketAddress *saddr;
  33
  34    saddr = g_new0(SocketAddress, 1);
  35    saddr->type = SOCKET_ADDRESS_TYPE_INET;
  36
  37    if (inet_parse(&saddr->u.inet, host_port, errp)) {
  38        qapi_free_SocketAddress(saddr);
  39        return NULL;
  40    }
  41
  42    return saddr;
  43}
  44
  45
  46static SocketAddress *unix_build_address(const char *path)
  47{
  48    SocketAddress *saddr;
  49
  50    saddr = g_new0(SocketAddress, 1);
  51    saddr->type = SOCKET_ADDRESS_TYPE_UNIX;
  52    saddr->u.q_unix.path = g_strdup(path);
  53
  54    return saddr;
  55}
  56
  57
  58struct SocketConnectData {
  59    MigrationState *s;
  60    char *hostname;
  61};
  62
  63static void socket_connect_data_free(void *opaque)
  64{
  65    struct SocketConnectData *data = opaque;
  66    if (!data) {
  67        return;
  68    }
  69    g_free(data->hostname);
  70    g_free(data);
  71}
  72
  73static void socket_outgoing_migration(QIOTask *task,
  74                                      gpointer opaque)
  75{
  76    struct SocketConnectData *data = opaque;
  77    QIOChannel *sioc = QIO_CHANNEL(qio_task_get_source(task));
  78    Error *err = NULL;
  79
  80    if (qio_task_propagate_error(task, &err)) {
  81        trace_migration_socket_outgoing_error(error_get_pretty(err));
  82        migrate_fd_error(data->s, err);
  83        error_free(err);
  84    } else {
  85        trace_migration_socket_outgoing_connected(data->hostname);
  86        migration_channel_connect(data->s, sioc, data->hostname);
  87    }
  88    object_unref(OBJECT(sioc));
  89}
  90
  91static void socket_start_outgoing_migration(MigrationState *s,
  92                                            SocketAddress *saddr,
  93                                            Error **errp)
  94{
  95    QIOChannelSocket *sioc = qio_channel_socket_new();
  96    struct SocketConnectData *data = g_new0(struct SocketConnectData, 1);
  97
  98    data->s = s;
  99    if (saddr->type == SOCKET_ADDRESS_TYPE_INET) {
 100        data->hostname = g_strdup(saddr->u.inet.host);
 101    }
 102
 103    qio_channel_set_name(QIO_CHANNEL(sioc), "migration-socket-outgoing");
 104    qio_channel_socket_connect_async(sioc,
 105                                     saddr,
 106                                     socket_outgoing_migration,
 107                                     data,
 108                                     socket_connect_data_free);
 109    qapi_free_SocketAddress(saddr);
 110}
 111
 112void tcp_start_outgoing_migration(MigrationState *s,
 113                                  const char *host_port,
 114                                  Error **errp)
 115{
 116    Error *err = NULL;
 117    SocketAddress *saddr = tcp_build_address(host_port, &err);
 118    if (!err) {
 119        socket_start_outgoing_migration(s, saddr, &err);
 120    }
 121    error_propagate(errp, err);
 122}
 123
 124void unix_start_outgoing_migration(MigrationState *s,
 125                                   const char *path,
 126                                   Error **errp)
 127{
 128    SocketAddress *saddr = unix_build_address(path);
 129    socket_start_outgoing_migration(s, saddr, errp);
 130}
 131
 132
 133static gboolean socket_accept_incoming_migration(QIOChannel *ioc,
 134                                                 GIOCondition condition,
 135                                                 gpointer opaque)
 136{
 137    QIOChannelSocket *sioc;
 138    Error *err = NULL;
 139
 140    sioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(ioc),
 141                                     &err);
 142    if (!sioc) {
 143        error_report("could not accept migration connection (%s)",
 144                     error_get_pretty(err));
 145        goto out;
 146    }
 147
 148    trace_migration_socket_incoming_accepted();
 149
 150    qio_channel_set_name(QIO_CHANNEL(sioc), "migration-socket-incoming");
 151    migration_channel_process_incoming(QIO_CHANNEL(sioc));
 152    object_unref(OBJECT(sioc));
 153
 154out:
 155    if (migration_has_all_channels()) {
 156        /* Close listening socket as its no longer needed */
 157        qio_channel_close(ioc, NULL);
 158        return G_SOURCE_REMOVE;
 159    } else {
 160        return G_SOURCE_CONTINUE;
 161    }
 162}
 163
 164
 165static void socket_start_incoming_migration(SocketAddress *saddr,
 166                                            Error **errp)
 167{
 168    QIOChannelSocket *listen_ioc = qio_channel_socket_new();
 169
 170    qio_channel_set_name(QIO_CHANNEL(listen_ioc),
 171                         "migration-socket-listener");
 172
 173    if (qio_channel_socket_listen_sync(listen_ioc, saddr, errp) < 0) {
 174        object_unref(OBJECT(listen_ioc));
 175        qapi_free_SocketAddress(saddr);
 176        return;
 177    }
 178
 179    qio_channel_add_watch(QIO_CHANNEL(listen_ioc),
 180                          G_IO_IN,
 181                          socket_accept_incoming_migration,
 182                          listen_ioc,
 183                          (GDestroyNotify)object_unref);
 184    qapi_free_SocketAddress(saddr);
 185}
 186
 187void tcp_start_incoming_migration(const char *host_port, Error **errp)
 188{
 189    Error *err = NULL;
 190    SocketAddress *saddr = tcp_build_address(host_port, &err);
 191    if (!err) {
 192        socket_start_incoming_migration(saddr, &err);
 193    }
 194    error_propagate(errp, err);
 195}
 196
 197void unix_start_incoming_migration(const char *path, Error **errp)
 198{
 199    SocketAddress *saddr = unix_build_address(path);
 200    socket_start_incoming_migration(saddr, errp);
 201}
 202