1 /* 2 * Serving QEMU block devices via NBD 3 * 4 * Copyright (c) 2012 Red Hat, Inc. 5 * 6 * Author: Paolo Bonzini <pbonzini@redhat.com> 7 * 8 * This work is licensed under the terms of the GNU GPL, version 2 or 9 * later. See the COPYING file in the top-level directory. 10 */ 11 12 #include "qemu/osdep.h" 13 #include "sysemu/blockdev.h" 14 #include "sysemu/block-backend.h" 15 #include "hw/block/block.h" 16 #include "qapi/qmp/qerror.h" 17 #include "sysemu/sysemu.h" 18 #include "qmp-commands.h" 19 #include "trace.h" 20 #include "block/nbd.h" 21 #include "io/channel-socket.h" 22 23 typedef struct NBDServerData { 24 QIOChannelSocket *listen_ioc; 25 int watch; 26 QCryptoTLSCreds *tlscreds; 27 } NBDServerData; 28 29 static NBDServerData *nbd_server; 30 31 32 static gboolean nbd_accept(QIOChannel *ioc, GIOCondition condition, 33 gpointer opaque) 34 { 35 QIOChannelSocket *cioc; 36 37 if (!nbd_server) { 38 return FALSE; 39 } 40 41 cioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(ioc), 42 NULL); 43 if (!cioc) { 44 return TRUE; 45 } 46 47 nbd_client_new(NULL, cioc, 48 nbd_server->tlscreds, NULL, 49 nbd_client_put); 50 object_unref(OBJECT(cioc)); 51 return TRUE; 52 } 53 54 55 static void nbd_server_free(NBDServerData *server) 56 { 57 if (!server) { 58 return; 59 } 60 61 if (server->watch != -1) { 62 g_source_remove(server->watch); 63 } 64 object_unref(OBJECT(server->listen_ioc)); 65 if (server->tlscreds) { 66 object_unref(OBJECT(server->tlscreds)); 67 } 68 69 g_free(server); 70 } 71 72 static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp) 73 { 74 Object *obj; 75 QCryptoTLSCreds *creds; 76 77 obj = object_resolve_path_component( 78 object_get_objects_root(), id); 79 if (!obj) { 80 error_setg(errp, "No TLS credentials with id '%s'", 81 id); 82 return NULL; 83 } 84 creds = (QCryptoTLSCreds *) 85 object_dynamic_cast(obj, TYPE_QCRYPTO_TLS_CREDS); 86 if (!creds) { 87 error_setg(errp, "Object with id '%s' is not TLS credentials", 88 id); 89 return NULL; 90 } 91 92 if (creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { 93 error_setg(errp, 94 "Expecting TLS credentials with a server endpoint"); 95 return NULL; 96 } 97 object_ref(obj); 98 return creds; 99 } 100 101 102 void qmp_nbd_server_start(SocketAddress *addr, 103 bool has_tls_creds, const char *tls_creds, 104 Error **errp) 105 { 106 if (nbd_server) { 107 error_setg(errp, "NBD server already running"); 108 return; 109 } 110 111 nbd_server = g_new0(NBDServerData, 1); 112 nbd_server->watch = -1; 113 nbd_server->listen_ioc = qio_channel_socket_new(); 114 if (qio_channel_socket_listen_sync( 115 nbd_server->listen_ioc, addr, errp) < 0) { 116 goto error; 117 } 118 119 if (has_tls_creds) { 120 nbd_server->tlscreds = nbd_get_tls_creds(tls_creds, errp); 121 if (!nbd_server->tlscreds) { 122 goto error; 123 } 124 125 if (addr->type != SOCKET_ADDRESS_KIND_INET) { 126 error_setg(errp, "TLS is only supported with IPv4/IPv6"); 127 goto error; 128 } 129 } 130 131 nbd_server->watch = qio_channel_add_watch( 132 QIO_CHANNEL(nbd_server->listen_ioc), 133 G_IO_IN, 134 nbd_accept, 135 NULL, 136 NULL); 137 138 return; 139 140 error: 141 nbd_server_free(nbd_server); 142 nbd_server = NULL; 143 } 144 145 void qmp_nbd_server_add(const char *device, bool has_writable, bool writable, 146 Error **errp) 147 { 148 BlockDriverState *bs = NULL; 149 BlockBackend *on_eject_blk; 150 NBDExport *exp; 151 152 if (!nbd_server) { 153 error_setg(errp, "NBD server not running"); 154 return; 155 } 156 157 if (nbd_export_find(device)) { 158 error_setg(errp, "NBD server already exporting device '%s'", device); 159 return; 160 } 161 162 on_eject_blk = blk_by_name(device); 163 164 bs = bdrv_lookup_bs(device, device, errp); 165 if (!bs) { 166 return; 167 } 168 169 if (!has_writable) { 170 writable = false; 171 } 172 if (bdrv_is_read_only(bs)) { 173 writable = false; 174 } 175 176 exp = nbd_export_new(bs, 0, -1, writable ? 0 : NBD_FLAG_READ_ONLY, 177 NULL, false, on_eject_blk, errp); 178 if (!exp) { 179 return; 180 } 181 182 nbd_export_set_name(exp, device); 183 184 /* The list of named exports has a strong reference to this export now and 185 * our only way of accessing it is through nbd_export_find(), so we can drop 186 * the strong reference that is @exp. */ 187 nbd_export_put(exp); 188 } 189 190 void qmp_nbd_server_stop(Error **errp) 191 { 192 nbd_export_close_all(); 193 194 nbd_server_free(nbd_server); 195 nbd_server = NULL; 196 } 197