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