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 BlockBackend *blk; 149 NBDExport *exp; 150 151 if (!nbd_server) { 152 error_setg(errp, "NBD server not running"); 153 return; 154 } 155 156 if (nbd_export_find(device)) { 157 error_setg(errp, "NBD server already exporting device '%s'", device); 158 return; 159 } 160 161 blk = blk_by_name(device); 162 if (!blk) { 163 error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, 164 "Device '%s' not found", device); 165 return; 166 } 167 if (!blk_is_inserted(blk)) { 168 error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, device); 169 return; 170 } 171 172 if (!has_writable) { 173 writable = false; 174 } 175 if (blk_is_read_only(blk)) { 176 writable = false; 177 } 178 179 exp = nbd_export_new(blk, 0, -1, writable ? 0 : NBD_FLAG_READ_ONLY, NULL, 180 errp); 181 if (!exp) { 182 return; 183 } 184 185 nbd_export_set_name(exp, device); 186 187 /* The list of named exports has a strong reference to this export now and 188 * our only way of accessing it is through nbd_export_find(), so we can drop 189 * the strong reference that is @exp. */ 190 nbd_export_put(exp); 191 } 192 193 void qmp_nbd_server_stop(Error **errp) 194 { 195 nbd_export_close_all(); 196 197 nbd_server_free(nbd_server); 198 nbd_server = NULL; 199 } 200