1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright (c) 2022 Dell Inc, or its subsidiaries. 3 * All rights reserved. 4 */ 5 6 #include "spdk/stdinc.h" 7 #include "spdk/version.h" 8 9 #include "spdk_internal/event.h" 10 11 #include "spdk/assert.h" 12 #include "spdk/config.h" 13 #include "spdk/env.h" 14 #include "spdk/init.h" 15 #include "spdk/log.h" 16 #include "spdk/thread.h" 17 #include "spdk/trace.h" 18 #include "spdk/string.h" 19 #include "spdk/scheduler.h" 20 #include "spdk/rpc.h" 21 #include "spdk/util.h" 22 #include "spdk/nvme.h" 23 #include "bdev_nvme.h" 24 25 #ifdef SPDK_CONFIG_AVAHI 26 #include <avahi-client/client.h> 27 #include <avahi-client/lookup.h> 28 #include <avahi-common/simple-watch.h> 29 #include <avahi-common/malloc.h> 30 #include <avahi-common/error.h> 31 32 static AvahiSimplePoll *g_avahi_simple_poll = NULL; 33 static AvahiClient *g_avahi_client = NULL; 34 35 struct mdns_discovery_entry_ctx { 36 char name[256]; 37 struct spdk_nvme_transport_id trid; 38 struct spdk_nvme_ctrlr_opts drv_opts; 39 TAILQ_ENTRY(mdns_discovery_entry_ctx) tailq; 40 struct mdns_discovery_ctx *ctx; 41 }; 42 43 struct mdns_discovery_ctx { 44 char *name; 45 char *svcname; 46 char *hostnqn; 47 AvahiServiceBrowser *sb; 48 struct spdk_poller *poller; 49 struct spdk_nvme_ctrlr_opts drv_opts; 50 struct nvme_ctrlr_opts bdev_opts; 51 uint32_t seqno; 52 bool stop; 53 struct spdk_thread *calling_thread; 54 TAILQ_ENTRY(mdns_discovery_ctx) tailq; 55 TAILQ_HEAD(, mdns_discovery_entry_ctx) mdns_discovery_entry_ctxs; 56 }; 57 58 TAILQ_HEAD(mdns_discovery_ctxs, mdns_discovery_ctx); 59 static struct mdns_discovery_ctxs g_mdns_discovery_ctxs = TAILQ_HEAD_INITIALIZER( 60 g_mdns_discovery_ctxs); 61 62 static struct mdns_discovery_entry_ctx * 63 create_mdns_discovery_entry_ctx(struct mdns_discovery_ctx *ctx, struct spdk_nvme_transport_id *trid) 64 { 65 struct mdns_discovery_entry_ctx *new_ctx; 66 67 assert(ctx); 68 assert(trid); 69 new_ctx = calloc(1, sizeof(*new_ctx)); 70 if (new_ctx == NULL) { 71 SPDK_ERRLOG("could not allocate new mdns_entry_ctx\n"); 72 return NULL; 73 } 74 75 new_ctx->ctx = ctx; 76 memcpy(&new_ctx->trid, trid, sizeof(struct spdk_nvme_transport_id)); 77 snprintf(new_ctx->name, sizeof(new_ctx->name), "%s%u_nvme", ctx->name, ctx->seqno); 78 memcpy(&new_ctx->drv_opts, &ctx->drv_opts, sizeof(ctx->drv_opts)); 79 snprintf(new_ctx->drv_opts.hostnqn, sizeof(ctx->drv_opts.hostnqn), "%s", ctx->hostnqn); 80 ctx->seqno = ctx->seqno + 1; 81 return new_ctx; 82 } 83 84 static void 85 mdns_bdev_nvme_start_discovery(void *_entry_ctx) 86 { 87 int status; 88 struct mdns_discovery_entry_ctx *entry_ctx = _entry_ctx; 89 90 assert(_entry_ctx); 91 status = bdev_nvme_start_discovery(&entry_ctx->trid, entry_ctx->name, 92 &entry_ctx->ctx->drv_opts, 93 &entry_ctx->ctx->bdev_opts, 94 0, true, NULL, NULL); 95 if (status) { 96 SPDK_ERRLOG("Error starting discovery for name %s addr %s port %s subnqn %s &trid %p\n", 97 entry_ctx->ctx->name, entry_ctx->trid.traddr, entry_ctx->trid.trsvcid, 98 entry_ctx->trid.subnqn, &entry_ctx->trid); 99 } 100 } 101 102 static void 103 free_mdns_discovery_entry_ctx(struct mdns_discovery_ctx *ctx) 104 { 105 struct mdns_discovery_entry_ctx *entry_ctx, *tmp; 106 107 if (!ctx) { 108 return; 109 } 110 111 TAILQ_FOREACH_SAFE(entry_ctx, &ctx->mdns_discovery_entry_ctxs, tailq, tmp) { 112 TAILQ_REMOVE(&ctx->mdns_discovery_entry_ctxs, entry_ctx, tailq); 113 free(entry_ctx); 114 } 115 } 116 117 static void 118 free_mdns_discovery_ctx(struct mdns_discovery_ctx *ctx) 119 { 120 if (!ctx) { 121 return; 122 } 123 124 free(ctx->name); 125 free(ctx->svcname); 126 free(ctx->hostnqn); 127 avahi_service_browser_free(ctx->sb); 128 free_mdns_discovery_entry_ctx(ctx); 129 free(ctx); 130 } 131 132 /* get_key_val_avahi_resolve_txt - Search for the key string in the TXT received 133 * from Avavi daemon and return its value. 134 * input 135 * txt: TXT returned by Ahavi daemon will be of format 136 * "NQN=nqn.1988-11.com.dell:SFSS:1:20221122170722e8" "p=tcp foo" and the 137 * AvahiStringList txt is a linked list with each node holding a 138 * key-value pair like key:p value:tcp 139 * 140 * key: Key string to search in the txt list 141 * output 142 * Returns the value for the key or NULL if key is not present 143 * Returned string needs to be freed with avahi_free() 144 */ 145 static char * 146 get_key_val_avahi_resolve_txt(AvahiStringList *txt, const char *key) 147 { 148 char *k = NULL, *v = NULL; 149 AvahiStringList *p = NULL; 150 int r; 151 152 if (!txt || !key) { 153 return NULL; 154 } 155 156 p = avahi_string_list_find(txt, key); 157 if (!p) { 158 return NULL; 159 } 160 161 r = avahi_string_list_get_pair(p, &k, &v, NULL); 162 if (r < 0) { 163 return NULL; 164 } 165 166 avahi_free(k); 167 return v; 168 } 169 170 static int 171 get_spdk_nvme_transport_from_proto_str(char *protocol, enum spdk_nvme_transport_type *trtype) 172 { 173 int status = -1; 174 175 if (!protocol || !trtype) { 176 return status; 177 } 178 179 if (strcmp("tcp", protocol) == 0) { 180 *trtype = SPDK_NVME_TRANSPORT_TCP; 181 return 0; 182 } 183 184 return status; 185 } 186 187 static enum spdk_nvmf_adrfam 188 get_spdk_nvme_adrfam_from_avahi_addr(const AvahiAddress *address) { 189 190 if (!address) 191 { 192 /* Return ipv4 by default */ 193 return SPDK_NVMF_ADRFAM_IPV4; 194 } 195 196 switch (address->proto) 197 { 198 case AVAHI_PROTO_INET: 199 return SPDK_NVMF_ADRFAM_IPV4; 200 case AVAHI_PROTO_INET6: 201 return SPDK_NVMF_ADRFAM_IPV6; 202 default: 203 return SPDK_NVMF_ADRFAM_IPV4; 204 } 205 } 206 207 static struct mdns_discovery_ctx * 208 get_mdns_discovery_ctx_by_svcname(const char *svcname) 209 { 210 struct mdns_discovery_ctx *ctx = NULL, *tmp_ctx = NULL; 211 212 if (!svcname) { 213 return NULL; 214 } 215 216 TAILQ_FOREACH_SAFE(ctx, &g_mdns_discovery_ctxs, tailq, tmp_ctx) { 217 if (strcmp(ctx->svcname, svcname) == 0) { 218 return ctx; 219 } 220 } 221 return NULL; 222 } 223 224 static void 225 mdns_resolve_handler( 226 AvahiServiceResolver *resolver, 227 AVAHI_GCC_UNUSED AvahiIfIndex intf, 228 AVAHI_GCC_UNUSED AvahiProtocol avahi_protocol, 229 AvahiResolverEvent resolve_event, 230 const char *svc_name, 231 const char *svc_type, 232 const char *svc_domain, 233 const char *host_name, 234 const AvahiAddress *host_address, 235 uint16_t port, 236 AvahiStringList *txt, 237 AvahiLookupResultFlags result_flags, 238 AVAHI_GCC_UNUSED void *user_data) 239 { 240 assert(resolver); 241 /* The handler gets called whenever a service has been resolved 242 successfully or timed out */ 243 switch (resolve_event) { 244 case AVAHI_RESOLVER_FOUND: { 245 char ipaddr[SPDK_NVMF_TRADDR_MAX_LEN + 1], port_str[SPDK_NVMF_TRSVCID_MAX_LEN + 1], *str; 246 struct spdk_nvme_transport_id *trid = NULL; 247 char *subnqn = NULL, *proto = NULL; 248 struct mdns_discovery_ctx *ctx = NULL; 249 struct mdns_discovery_entry_ctx *entry_ctx = NULL; 250 int status = -1; 251 252 memset(ipaddr, 0, sizeof(ipaddr)); 253 memset(port_str, 0, sizeof(port_str)); 254 SPDK_INFOLOG(bdev_nvme, "Service '%s' of type '%s' in domain '%s'\n", svc_name, svc_type, 255 svc_domain); 256 avahi_address_snprint(ipaddr, sizeof(ipaddr), host_address); 257 snprintf(port_str, sizeof(port_str), "%d", port); 258 str = avahi_string_list_to_string(txt); 259 SPDK_INFOLOG(bdev_nvme, 260 "\t%s:%u (%s)\n" 261 "\tTXT=%s\n" 262 "\tcookie is %u\n" 263 "\tis_local: %i\n" 264 "\tour_own: %i\n" 265 "\twide_area: %i\n" 266 "\tmulticast: %i\n" 267 "\tcached: %i\n", 268 host_name, port, ipaddr, 269 str, 270 avahi_string_list_get_service_cookie(txt), 271 !!(result_flags & AVAHI_LOOKUP_RESULT_LOCAL), 272 !!(result_flags & AVAHI_LOOKUP_RESULT_OUR_OWN), 273 !!(result_flags & AVAHI_LOOKUP_RESULT_WIDE_AREA), 274 !!(result_flags & AVAHI_LOOKUP_RESULT_MULTICAST), 275 !!(result_flags & AVAHI_LOOKUP_RESULT_CACHED)); 276 avahi_free(str); 277 278 ctx = get_mdns_discovery_ctx_by_svcname(svc_type); 279 if (!ctx) { 280 SPDK_ERRLOG("Unknown Service '%s'\n", svc_type); 281 break; 282 } 283 284 trid = (struct spdk_nvme_transport_id *) calloc(1, sizeof(struct spdk_nvme_transport_id)); 285 if (!trid) { 286 SPDK_ERRLOG(" Error allocating memory for trid\n"); 287 break; 288 } 289 trid->adrfam = get_spdk_nvme_adrfam_from_avahi_addr(host_address); 290 if (trid->adrfam != SPDK_NVMF_ADRFAM_IPV4) { 291 /* TODO: For now process only ipv4 addresses */ 292 SPDK_INFOLOG(bdev_nvme, "trid family is not IPV4 %d\n", trid->adrfam); 293 free(trid); 294 break; 295 } 296 subnqn = get_key_val_avahi_resolve_txt(txt, "NQN"); 297 if (!subnqn) { 298 free(trid); 299 SPDK_ERRLOG("subnqn received is empty for service %s\n", ctx->svcname); 300 break; 301 } 302 proto = get_key_val_avahi_resolve_txt(txt, "p"); 303 if (!proto) { 304 free(trid); 305 avahi_free(subnqn); 306 SPDK_ERRLOG("Protocol not received for service %s\n", ctx->svcname); 307 break; 308 } 309 status = get_spdk_nvme_transport_from_proto_str(proto, &trid->trtype); 310 if (status) { 311 free(trid); 312 avahi_free(subnqn); 313 avahi_free(proto); 314 SPDK_ERRLOG("Unable to derive nvme transport type for service %s\n", ctx->svcname); 315 break; 316 } 317 snprintf(trid->traddr, sizeof(trid->traddr), "%s", ipaddr); 318 snprintf(trid->trsvcid, sizeof(trid->trsvcid), "%s", port_str); 319 snprintf(trid->subnqn, sizeof(trid->subnqn), "%s", subnqn); 320 TAILQ_FOREACH(entry_ctx, &ctx->mdns_discovery_entry_ctxs, tailq) { 321 if (!spdk_nvme_transport_id_compare(trid, &entry_ctx->trid)) { 322 SPDK_ERRLOG("mDNS discovery entry exists already. trid->traddr: %s trid->trsvcid: %s\n", 323 trid->traddr, trid->trsvcid); 324 free(trid); 325 avahi_free(subnqn); 326 avahi_free(proto); 327 avahi_service_resolver_free(resolver); 328 return; 329 } 330 } 331 entry_ctx = create_mdns_discovery_entry_ctx(ctx, trid); 332 TAILQ_INSERT_TAIL(&ctx->mdns_discovery_entry_ctxs, entry_ctx, tailq); 333 spdk_thread_send_msg(ctx->calling_thread, mdns_bdev_nvme_start_discovery, entry_ctx); 334 free(trid); 335 avahi_free(subnqn); 336 avahi_free(proto); 337 break; 338 } 339 case AVAHI_RESOLVER_FAILURE: 340 SPDK_ERRLOG("(Resolver) Failed to resolve service '%s' of type '%s' in domain '%s': %s\n", 341 svc_name, svc_type, svc_domain, 342 avahi_strerror(avahi_client_errno(avahi_service_resolver_get_client(resolver)))); 343 break; 344 default: 345 SPDK_ERRLOG("Unknown Avahi resolver event: %d", resolve_event); 346 } 347 avahi_service_resolver_free(resolver); 348 } 349 350 static void 351 mdns_browse_handler( 352 AvahiServiceBrowser *browser, 353 AvahiIfIndex intf, 354 AvahiProtocol avahi_protocol, 355 AvahiBrowserEvent browser_event, 356 const char *svc_name, 357 const char *svc_type, 358 const char *svc_domain, 359 AVAHI_GCC_UNUSED AvahiLookupResultFlags result_flags, 360 void *user_data) 361 { 362 AvahiClient *client = user_data; 363 364 assert(browser); 365 /* The handler gets called whenever a new service becomes available 366 or removed from the LAN */ 367 switch (browser_event) { 368 case AVAHI_BROWSER_NEW: 369 SPDK_DEBUGLOG(bdev_nvme, "(Browser) NEW: service '%s' of type '%s' in domain '%s'\n", svc_name, 370 svc_type, 371 svc_domain); 372 /* We ignore the returned resolver object. In the callback 373 function we free it. If the server is terminated before 374 the callback function is called the server will free 375 the resolver for us. */ 376 if (!(avahi_service_resolver_new(client, intf, avahi_protocol, svc_name, svc_type, svc_domain, 377 AVAHI_PROTO_UNSPEC, 0, 378 mdns_resolve_handler, client))) { 379 SPDK_ERRLOG("Failed to resolve service '%s': %s\n", svc_name, 380 avahi_strerror(avahi_client_errno(client))); 381 } 382 break; 383 case AVAHI_BROWSER_REMOVE: 384 SPDK_ERRLOG("(Browser) REMOVE: service '%s' of type '%s' in domain '%s'\n", svc_name, svc_type, 385 svc_domain); 386 /* On remove, we are not doing the automatic cleanup of connections 387 * to the targets that were learnt from the CDC, for which remove event has 388 * been received. If required, user can clear the connections manually by 389 * invoking bdev_nvme_stop_discovery. We can implement the automatic cleanup 390 * later, if there is a requirement in the future. 391 */ 392 break; 393 case AVAHI_BROWSER_ALL_FOR_NOW: 394 case AVAHI_BROWSER_CACHE_EXHAUSTED: 395 SPDK_INFOLOG(bdev_nvme, "(Browser) %s\n", 396 browser_event == AVAHI_BROWSER_CACHE_EXHAUSTED ? "CACHE_EXHAUSTED" : "ALL_FOR_NOW"); 397 break; 398 case AVAHI_BROWSER_FAILURE: 399 SPDK_ERRLOG("(Browser) Failure: %s\n", 400 avahi_strerror(avahi_client_errno(avahi_service_browser_get_client(browser)))); 401 return; 402 default: 403 SPDK_ERRLOG("Unknown Avahi browser event: %d", browser_event); 404 } 405 } 406 407 static void 408 client_handler(AvahiClient *client, AvahiClientState avahi_state, AVAHI_GCC_UNUSED void *user_data) 409 { 410 assert(client); 411 /* The handler gets called whenever the client or server state changes */ 412 if (avahi_state == AVAHI_CLIENT_FAILURE) { 413 SPDK_ERRLOG("Server connection failure: %s\n", avahi_strerror(avahi_client_errno(client))); 414 } 415 } 416 417 static int 418 bdev_nvme_avahi_iterate(void *arg) 419 { 420 struct mdns_discovery_ctx *ctx = arg; 421 int rc; 422 423 if (ctx->stop) { 424 SPDK_INFOLOG(bdev_nvme, "Stopping avahi poller for service %s\n", ctx->svcname); 425 spdk_poller_unregister(&ctx->poller); 426 TAILQ_REMOVE(&g_mdns_discovery_ctxs, ctx, tailq); 427 free_mdns_discovery_ctx(ctx); 428 return SPDK_POLLER_IDLE; 429 } 430 431 if (g_avahi_simple_poll == NULL) { 432 spdk_poller_unregister(&ctx->poller); 433 return SPDK_POLLER_IDLE; 434 } 435 436 rc = avahi_simple_poll_iterate(g_avahi_simple_poll, 0); 437 if (rc && rc != -EAGAIN) { 438 SPDK_ERRLOG("avahi poll returned error for service: %s/n", ctx->svcname); 439 return SPDK_POLLER_IDLE; 440 } 441 442 return SPDK_POLLER_BUSY; 443 } 444 445 static void 446 start_mdns_discovery_poller(void *arg) 447 { 448 struct mdns_discovery_ctx *ctx = arg; 449 450 assert(arg); 451 TAILQ_INSERT_TAIL(&g_mdns_discovery_ctxs, ctx, tailq); 452 ctx->poller = SPDK_POLLER_REGISTER(bdev_nvme_avahi_iterate, ctx, 100 * 1000); 453 } 454 455 int 456 bdev_nvme_start_mdns_discovery(const char *base_name, 457 const char *svcname, 458 struct spdk_nvme_ctrlr_opts *drv_opts, 459 struct nvme_ctrlr_opts *bdev_opts) 460 { 461 AvahiServiceBrowser *sb = NULL; 462 int error; 463 struct mdns_discovery_ctx *ctx; 464 465 assert(base_name); 466 assert(svcname); 467 468 TAILQ_FOREACH(ctx, &g_mdns_discovery_ctxs, tailq) { 469 if (strcmp(ctx->name, base_name) == 0) { 470 SPDK_ERRLOG("mDNS discovery already running with name %s\n", base_name); 471 return -EEXIST; 472 } 473 474 if (strcmp(ctx->svcname, svcname) == 0) { 475 SPDK_ERRLOG("mDNS discovery already running for service %s\n", svcname); 476 return -EEXIST; 477 } 478 } 479 480 if (g_avahi_simple_poll == NULL) { 481 482 /* Allocate main loop object */ 483 if (!(g_avahi_simple_poll = avahi_simple_poll_new())) { 484 SPDK_ERRLOG("Failed to create poll object for mDNS discovery for service: %s.\n", svcname); 485 return -ENOMEM; 486 } 487 } 488 489 if (g_avahi_client == NULL) { 490 491 /* Allocate a new client */ 492 g_avahi_client = avahi_client_new(avahi_simple_poll_get(g_avahi_simple_poll), 0, client_handler, 493 NULL, &error); 494 /* Check whether creating the client object succeeded */ 495 if (!g_avahi_client) { 496 SPDK_ERRLOG("Failed to create mDNS client for service:%s Error: %s\n", svcname, 497 avahi_strerror(error)); 498 return -ENOMEM; 499 } 500 } 501 502 /* Create the service browser */ 503 if (!(sb = avahi_service_browser_new(g_avahi_client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, svcname, 504 NULL, 0, mdns_browse_handler, g_avahi_client))) { 505 SPDK_ERRLOG("Failed to create service browser for service: %s Error: %s\n", svcname, 506 avahi_strerror(avahi_client_errno(g_avahi_client))); 507 return -ENOMEM; 508 } 509 510 ctx = calloc(1, sizeof(*ctx)); 511 if (ctx == NULL) { 512 SPDK_ERRLOG("Error creating mDNS discovery ctx for service: %s\n", svcname); 513 avahi_service_browser_free(sb); 514 return -ENOMEM; 515 } 516 517 ctx->svcname = strdup(svcname); 518 if (ctx->svcname == NULL) { 519 SPDK_ERRLOG("Error creating mDNS discovery ctx svcname for service: %s\n", svcname); 520 free_mdns_discovery_ctx(ctx); 521 avahi_service_browser_free(sb); 522 return -ENOMEM; 523 } 524 ctx->name = strdup(base_name); 525 if (ctx->name == NULL) { 526 SPDK_ERRLOG("Error creating mDNS discovery ctx name for service: %s\n", svcname); 527 free_mdns_discovery_ctx(ctx); 528 avahi_service_browser_free(sb); 529 return -ENOMEM; 530 } 531 memcpy(&ctx->drv_opts, drv_opts, sizeof(*drv_opts)); 532 memcpy(&ctx->bdev_opts, bdev_opts, sizeof(*bdev_opts)); 533 ctx->sb = sb; 534 ctx->calling_thread = spdk_get_thread(); 535 TAILQ_INIT(&ctx->mdns_discovery_entry_ctxs); 536 /* Even if user did not specify hostnqn, we can still strdup("\0"); */ 537 ctx->hostnqn = strdup(ctx->drv_opts.hostnqn); 538 if (ctx->hostnqn == NULL) { 539 SPDK_ERRLOG("Error creating mDNS discovery ctx hostnqn for service: %s\n", svcname); 540 free_mdns_discovery_ctx(ctx); 541 return -ENOMEM; 542 } 543 /* Start the poller for the Avahi client browser in g_bdev_nvme_init_thread */ 544 spdk_thread_send_msg(g_bdev_nvme_init_thread, start_mdns_discovery_poller, ctx); 545 return 0; 546 } 547 548 static void 549 mdns_stop_discovery_entry(struct mdns_discovery_ctx *ctx) 550 { 551 struct mdns_discovery_entry_ctx *entry_ctx = NULL; 552 553 assert(ctx); 554 555 TAILQ_FOREACH(entry_ctx, &ctx->mdns_discovery_entry_ctxs, tailq) { 556 bdev_nvme_stop_discovery(entry_ctx->name, NULL, NULL); 557 } 558 } 559 560 int 561 bdev_nvme_stop_mdns_discovery(const char *name) 562 { 563 struct mdns_discovery_ctx *ctx; 564 565 assert(name); 566 TAILQ_FOREACH(ctx, &g_mdns_discovery_ctxs, tailq) { 567 if (strcmp(name, ctx->name) == 0) { 568 if (ctx->stop) { 569 return -EALREADY; 570 } 571 /* set stop to true to stop the mdns poller instance */ 572 ctx->stop = true; 573 mdns_stop_discovery_entry(ctx); 574 return 0; 575 } 576 } 577 578 return -ENOENT; 579 } 580 581 void 582 bdev_nvme_get_mdns_discovery_info(struct spdk_jsonrpc_request *request) 583 { 584 struct mdns_discovery_ctx *ctx; 585 struct mdns_discovery_entry_ctx *entry_ctx; 586 struct spdk_json_write_ctx *w; 587 588 w = spdk_jsonrpc_begin_result(request); 589 spdk_json_write_array_begin(w); 590 TAILQ_FOREACH(ctx, &g_mdns_discovery_ctxs, tailq) { 591 spdk_json_write_object_begin(w); 592 spdk_json_write_named_string(w, "name", ctx->name); 593 spdk_json_write_named_string(w, "svcname", ctx->svcname); 594 595 spdk_json_write_named_array_begin(w, "referrals"); 596 TAILQ_FOREACH(entry_ctx, &ctx->mdns_discovery_entry_ctxs, tailq) { 597 spdk_json_write_object_begin(w); 598 spdk_json_write_named_string(w, "name", entry_ctx->name); 599 spdk_json_write_named_object_begin(w, "trid"); 600 nvme_bdev_dump_trid_json(&entry_ctx->trid, w); 601 spdk_json_write_object_end(w); 602 spdk_json_write_object_end(w); 603 } 604 spdk_json_write_array_end(w); 605 606 spdk_json_write_object_end(w); 607 } 608 spdk_json_write_array_end(w); 609 spdk_jsonrpc_end_result(request, w); 610 } 611 612 void 613 bdev_nvme_mdns_discovery_config_json(struct spdk_json_write_ctx *w) 614 { 615 struct mdns_discovery_ctx *ctx; 616 617 TAILQ_FOREACH(ctx, &g_mdns_discovery_ctxs, tailq) { 618 spdk_json_write_object_begin(w); 619 620 spdk_json_write_named_string(w, "method", "bdev_nvme_start_mdns_discovery"); 621 622 spdk_json_write_named_object_begin(w, "params"); 623 spdk_json_write_named_string(w, "name", ctx->name); 624 spdk_json_write_named_string(w, "svcname", ctx->svcname); 625 spdk_json_write_named_string(w, "hostnqn", ctx->hostnqn); 626 spdk_json_write_object_end(w); 627 628 spdk_json_write_object_end(w); 629 } 630 } 631 632 #else /* SPDK_CONFIG_AVAHI */ 633 634 int 635 bdev_nvme_start_mdns_discovery(const char *base_name, 636 const char *svcname, 637 struct spdk_nvme_ctrlr_opts *drv_opts, 638 struct nvme_ctrlr_opts *bdev_opts) 639 { 640 SPDK_ERRLOG("spdk not built with --with-avahi option\n"); 641 return -ENOTSUP; 642 } 643 644 int 645 bdev_nvme_stop_mdns_discovery(const char *name) 646 { 647 SPDK_ERRLOG("spdk not built with --with-avahi option\n"); 648 return -ENOTSUP; 649 } 650 651 void 652 bdev_nvme_get_mdns_discovery_info(struct spdk_jsonrpc_request *request) 653 { 654 SPDK_ERRLOG("spdk not built with --with-avahi option\n"); 655 spdk_jsonrpc_send_error_response(request, -ENOTSUP, spdk_strerror(ENOTSUP)); 656 } 657 658 void 659 bdev_nvme_mdns_discovery_config_json(struct spdk_json_write_ctx *w) 660 { 661 /* Empty function to be invoked, when SPDK is built without --with-avahi */ 662 } 663 664 #endif 665