blockdev-nbd.c 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  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. #include "qemu/osdep.h"
  12. #include "sysemu/blockdev.h"
  13. #include "sysemu/block-backend.h"
  14. #include "hw/block/block.h"
  15. #include "qapi/qmp/qerror.h"
  16. #include "sysemu/sysemu.h"
  17. #include "qmp-commands.h"
  18. #include "block/nbd.h"
  19. #include "io/channel-socket.h"
  20. typedef struct NBDServerData {
  21. QIOChannelSocket *listen_ioc;
  22. int watch;
  23. QCryptoTLSCreds *tlscreds;
  24. } NBDServerData;
  25. static NBDServerData *nbd_server;
  26. static void nbd_blockdev_client_closed(NBDClient *client, bool ignored)
  27. {
  28. nbd_client_put(client);
  29. }
  30. static gboolean nbd_accept(QIOChannel *ioc, GIOCondition condition,
  31. gpointer opaque)
  32. {
  33. QIOChannelSocket *cioc;
  34. if (!nbd_server) {
  35. return FALSE;
  36. }
  37. cioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(ioc),
  38. NULL);
  39. if (!cioc) {
  40. return TRUE;
  41. }
  42. qio_channel_set_name(QIO_CHANNEL(cioc), "nbd-server");
  43. nbd_client_new(NULL, cioc,
  44. nbd_server->tlscreds, NULL,
  45. nbd_blockdev_client_closed);
  46. object_unref(OBJECT(cioc));
  47. return TRUE;
  48. }
  49. static void nbd_server_free(NBDServerData *server)
  50. {
  51. if (!server) {
  52. return;
  53. }
  54. if (server->watch != -1) {
  55. g_source_remove(server->watch);
  56. }
  57. object_unref(OBJECT(server->listen_ioc));
  58. if (server->tlscreds) {
  59. object_unref(OBJECT(server->tlscreds));
  60. }
  61. g_free(server);
  62. }
  63. static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp)
  64. {
  65. Object *obj;
  66. QCryptoTLSCreds *creds;
  67. obj = object_resolve_path_component(
  68. object_get_objects_root(), id);
  69. if (!obj) {
  70. error_setg(errp, "No TLS credentials with id '%s'",
  71. id);
  72. return NULL;
  73. }
  74. creds = (QCryptoTLSCreds *)
  75. object_dynamic_cast(obj, TYPE_QCRYPTO_TLS_CREDS);
  76. if (!creds) {
  77. error_setg(errp, "Object with id '%s' is not TLS credentials",
  78. id);
  79. return NULL;
  80. }
  81. if (creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
  82. error_setg(errp,
  83. "Expecting TLS credentials with a server endpoint");
  84. return NULL;
  85. }
  86. object_ref(obj);
  87. return creds;
  88. }
  89. void qmp_nbd_server_start(SocketAddress *addr,
  90. bool has_tls_creds, const char *tls_creds,
  91. Error **errp)
  92. {
  93. if (nbd_server) {
  94. error_setg(errp, "NBD server already running");
  95. return;
  96. }
  97. nbd_server = g_new0(NBDServerData, 1);
  98. nbd_server->watch = -1;
  99. nbd_server->listen_ioc = qio_channel_socket_new();
  100. qio_channel_set_name(QIO_CHANNEL(nbd_server->listen_ioc),
  101. "nbd-listener");
  102. if (qio_channel_socket_listen_sync(
  103. nbd_server->listen_ioc, addr, errp) < 0) {
  104. goto error;
  105. }
  106. if (has_tls_creds) {
  107. nbd_server->tlscreds = nbd_get_tls_creds(tls_creds, errp);
  108. if (!nbd_server->tlscreds) {
  109. goto error;
  110. }
  111. /* TODO SOCKET_ADDRESS_KIND_FD where fd has AF_INET or AF_INET6 */
  112. if (addr->type != SOCKET_ADDRESS_KIND_INET) {
  113. error_setg(errp, "TLS is only supported with IPv4/IPv6");
  114. goto error;
  115. }
  116. }
  117. nbd_server->watch = qio_channel_add_watch(
  118. QIO_CHANNEL(nbd_server->listen_ioc),
  119. G_IO_IN,
  120. nbd_accept,
  121. NULL,
  122. NULL);
  123. return;
  124. error:
  125. nbd_server_free(nbd_server);
  126. nbd_server = NULL;
  127. }
  128. void qmp_nbd_server_add(const char *device, bool has_writable, bool writable,
  129. Error **errp)
  130. {
  131. BlockDriverState *bs = NULL;
  132. BlockBackend *on_eject_blk;
  133. NBDExport *exp;
  134. if (!nbd_server) {
  135. error_setg(errp, "NBD server not running");
  136. return;
  137. }
  138. if (nbd_export_find(device)) {
  139. error_setg(errp, "NBD server already exporting device '%s'", device);
  140. return;
  141. }
  142. on_eject_blk = blk_by_name(device);
  143. bs = bdrv_lookup_bs(device, device, errp);
  144. if (!bs) {
  145. return;
  146. }
  147. if (!has_writable) {
  148. writable = false;
  149. }
  150. if (bdrv_is_read_only(bs)) {
  151. writable = false;
  152. }
  153. exp = nbd_export_new(bs, 0, -1, writable ? 0 : NBD_FLAG_READ_ONLY,
  154. NULL, false, on_eject_blk, errp);
  155. if (!exp) {
  156. return;
  157. }
  158. nbd_export_set_name(exp, device);
  159. /* The list of named exports has a strong reference to this export now and
  160. * our only way of accessing it is through nbd_export_find(), so we can drop
  161. * the strong reference that is @exp. */
  162. nbd_export_put(exp);
  163. }
  164. void qmp_nbd_server_stop(Error **errp)
  165. {
  166. nbd_export_close_all();
  167. nbd_server_free(nbd_server);
  168. nbd_server = NULL;
  169. }