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