xref: /spdk/lib/iscsi/init_grp.c (revision 8a0a98d35e21f282088edf28b9e8da66ec390e3a)
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright (C) 2008-2012 Daisuke Aoyama <aoyama@peach.ne.jp>.
5  *   Copyright (c) Intel Corporation.
6  *   All rights reserved.
7  *
8  *   Redistribution and use in source and binary forms, with or without
9  *   modification, are permitted provided that the following conditions
10  *   are met:
11  *
12  *     * Redistributions of source code must retain the above copyright
13  *       notice, this list of conditions and the following disclaimer.
14  *     * Redistributions in binary form must reproduce the above copyright
15  *       notice, this list of conditions and the following disclaimer in
16  *       the documentation and/or other materials provided with the
17  *       distribution.
18  *     * Neither the name of Intel Corporation nor the names of its
19  *       contributors may be used to endorse or promote products derived
20  *       from this software without specific prior written permission.
21  *
22  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
25  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
28  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33  */
34 
35 #include "spdk/stdinc.h"
36 
37 #include "spdk/conf.h"
38 #include "spdk/string.h"
39 
40 #include "spdk_internal/log.h"
41 
42 #include "iscsi/iscsi.h"
43 #include "iscsi/init_grp.h"
44 
45 static struct spdk_iscsi_init_grp *
46 spdk_iscsi_init_grp_create(int tag)
47 {
48 	struct spdk_iscsi_init_grp *ig;
49 
50 	ig = calloc(1, sizeof(*ig));
51 	if (ig == NULL) {
52 		SPDK_ERRLOG("calloc() failed for initiator group\n");
53 		return NULL;
54 	}
55 
56 	ig->tag = tag;
57 	TAILQ_INIT(&ig->initiator_head);
58 	TAILQ_INIT(&ig->netmask_head);
59 	return ig;
60 }
61 
62 static struct spdk_iscsi_initiator_name *
63 spdk_iscsi_init_grp_find_initiator(struct spdk_iscsi_init_grp *ig, char *name)
64 {
65 	struct spdk_iscsi_initiator_name *iname;
66 
67 	TAILQ_FOREACH(iname, &ig->initiator_head, tailq) {
68 		if (!strcmp(iname->name, name)) {
69 			return iname;
70 		}
71 	}
72 	return NULL;
73 }
74 
75 static int
76 spdk_iscsi_init_grp_add_initiator(struct spdk_iscsi_init_grp *ig, char *name)
77 {
78 	struct spdk_iscsi_initiator_name *iname;
79 	char *p;
80 
81 	if (ig->ninitiators >= MAX_INITIATOR) {
82 		SPDK_ERRLOG("> MAX_INITIATOR(=%d) is not allowed\n", MAX_INITIATOR);
83 		return -EPERM;
84 	}
85 
86 	iname = spdk_iscsi_init_grp_find_initiator(ig, name);
87 	if (iname != NULL) {
88 		return -EEXIST;
89 	}
90 
91 	iname = malloc(sizeof(*iname));
92 	if (iname == NULL) {
93 		SPDK_ERRLOG("malloc() failed for initiator name str\n");
94 		return -ENOMEM;
95 	}
96 
97 	iname->name = strdup(name);
98 	if (iname->name == NULL) {
99 		SPDK_ERRLOG("strdup() failed for initiator name\n");
100 		free(iname);
101 		return -ENOMEM;
102 	}
103 
104 	/* Replace "ALL" by "ANY" if set */
105 	p = strstr(iname->name, "ALL");
106 	if (p != NULL) {
107 		SPDK_WARNLOG("Please use \"%s\" instead of \"%s\"\n", "ANY", "ALL");
108 		SPDK_WARNLOG("Converting \"%s\" to \"%s\" automatically\n", "ALL", "ANY");
109 		memcpy(p, "ANY", 3);
110 	}
111 
112 	TAILQ_INSERT_TAIL(&ig->initiator_head, iname, tailq);
113 	ig->ninitiators++;
114 
115 	SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "InitiatorName %s\n", name);
116 	return 0;
117 }
118 
119 static int
120 spdk_iscsi_init_grp_delete_initiator(struct spdk_iscsi_init_grp *ig, char *name)
121 {
122 	struct spdk_iscsi_initiator_name *iname;
123 
124 	iname = spdk_iscsi_init_grp_find_initiator(ig, name);
125 	if (iname == NULL) {
126 		return -ENOENT;
127 	}
128 
129 	TAILQ_REMOVE(&ig->initiator_head, iname, tailq);
130 	ig->ninitiators--;
131 	free(iname->name);
132 	free(iname);
133 	return 0;
134 }
135 
136 static int
137 spdk_iscsi_init_grp_add_initiators(struct spdk_iscsi_init_grp *ig, int num_inames, char **inames)
138 {
139 	int i;
140 	int rc;
141 
142 	for (i = 0; i < num_inames; i++) {
143 		rc = spdk_iscsi_init_grp_add_initiator(ig, inames[i]);
144 		if (rc < 0) {
145 			goto cleanup;
146 		}
147 	}
148 	return 0;
149 
150 cleanup:
151 	for (; i > 0; --i) {
152 		spdk_iscsi_init_grp_delete_initiator(ig, inames[i - 1]);
153 	}
154 	return rc;
155 }
156 
157 static void
158 spdk_iscsi_init_grp_delete_all_initiators(struct spdk_iscsi_init_grp *ig)
159 {
160 	struct spdk_iscsi_initiator_name *iname, *tmp;
161 
162 	TAILQ_FOREACH_SAFE(iname, &ig->initiator_head, tailq, tmp) {
163 		TAILQ_REMOVE(&ig->initiator_head, iname, tailq);
164 		ig->ninitiators--;
165 		free(iname->name);
166 		free(iname);
167 	}
168 }
169 
170 static int
171 spdk_iscsi_init_grp_delete_initiators(struct spdk_iscsi_init_grp *ig, int num_inames, char **inames)
172 {
173 	int i;
174 	int rc;
175 
176 	for (i = 0; i < num_inames; i++) {
177 		rc = spdk_iscsi_init_grp_delete_initiator(ig, inames[i]);
178 		if (rc < 0) {
179 			goto cleanup;
180 		}
181 	}
182 	return 0;
183 
184 cleanup:
185 	for (; i > 0; --i) {
186 		rc = spdk_iscsi_init_grp_add_initiator(ig, inames[i - 1]);
187 		if (rc != 0) {
188 			spdk_iscsi_init_grp_delete_all_initiators(ig);
189 			break;
190 		}
191 	}
192 	return -1;
193 }
194 
195 static struct spdk_iscsi_initiator_netmask *
196 spdk_iscsi_init_grp_find_netmask(struct spdk_iscsi_init_grp *ig, const char *mask)
197 {
198 	struct spdk_iscsi_initiator_netmask *netmask;
199 
200 	TAILQ_FOREACH(netmask, &ig->netmask_head, tailq) {
201 		if (!strcmp(netmask->mask, mask)) {
202 			return netmask;
203 		}
204 	}
205 	return NULL;
206 }
207 
208 static int
209 spdk_iscsi_init_grp_add_netmask(struct spdk_iscsi_init_grp *ig, char *mask)
210 {
211 	struct spdk_iscsi_initiator_netmask *imask;
212 	char *p;
213 
214 	if (ig->nnetmasks >= MAX_NETMASK) {
215 		SPDK_ERRLOG("> MAX_NETMASK(=%d) is not allowed\n", MAX_NETMASK);
216 		return -EPERM;
217 	}
218 
219 	imask = spdk_iscsi_init_grp_find_netmask(ig, mask);
220 	if (imask != NULL) {
221 		return -EEXIST;
222 	}
223 
224 	imask = malloc(sizeof(*imask));
225 	if (imask == NULL) {
226 		SPDK_ERRLOG("malloc() failed for inititator mask str\n");
227 		return -ENOMEM;
228 	}
229 
230 	imask->mask = strdup(mask);
231 	if (imask->mask == NULL) {
232 		SPDK_ERRLOG("strdup() failed for initiator mask\n");
233 		free(imask);
234 		return -ENOMEM;
235 	}
236 
237 	/* Replace "ALL" by "ANY" if set */
238 	p = strstr(imask->mask, "ALL");
239 	if (p != NULL) {
240 		SPDK_WARNLOG("Please use \"%s\" instead of \"%s\"\n", "ANY", "ALL");
241 		SPDK_WARNLOG("Converting \"%s\" to \"%s\" automatically\n", "ALL", "ANY");
242 		memcpy(p, "ANY", 3);
243 	}
244 
245 	TAILQ_INSERT_TAIL(&ig->netmask_head, imask, tailq);
246 	ig->nnetmasks++;
247 
248 	SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "Netmask %s\n", mask);
249 	return 0;
250 }
251 
252 static int
253 spdk_iscsi_init_grp_delete_netmask(struct spdk_iscsi_init_grp *ig, char *mask)
254 {
255 	struct spdk_iscsi_initiator_netmask *imask;
256 
257 	imask = spdk_iscsi_init_grp_find_netmask(ig, mask);
258 	if (imask == NULL) {
259 		return -ENOENT;
260 	}
261 
262 	TAILQ_REMOVE(&ig->netmask_head, imask, tailq);
263 	ig->nnetmasks--;
264 	free(imask->mask);
265 	free(imask);
266 	return 0;
267 }
268 
269 static int
270 spdk_iscsi_init_grp_add_netmasks(struct spdk_iscsi_init_grp *ig, int num_imasks, char **imasks)
271 {
272 	int i;
273 	int rc;
274 
275 	for (i = 0; i < num_imasks; i++) {
276 		rc = spdk_iscsi_init_grp_add_netmask(ig, imasks[i]);
277 		if (rc != 0) {
278 			goto cleanup;
279 		}
280 	}
281 	return 0;
282 
283 cleanup:
284 	for (; i > 0; --i) {
285 		spdk_iscsi_init_grp_delete_netmask(ig, imasks[i - 1]);
286 	}
287 	return rc;
288 }
289 
290 static void
291 spdk_iscsi_init_grp_delete_all_netmasks(struct spdk_iscsi_init_grp *ig)
292 {
293 	struct spdk_iscsi_initiator_netmask *imask, *tmp;
294 
295 	TAILQ_FOREACH_SAFE(imask, &ig->netmask_head, tailq, tmp) {
296 		TAILQ_REMOVE(&ig->netmask_head, imask, tailq);
297 		ig->nnetmasks--;
298 		free(imask->mask);
299 		free(imask);
300 	}
301 }
302 
303 static int
304 spdk_iscsi_init_grp_delete_netmasks(struct spdk_iscsi_init_grp *ig, int num_imasks, char **imasks)
305 {
306 	int i;
307 	int rc;
308 
309 	for (i = 0; i < num_imasks; i++) {
310 		rc = spdk_iscsi_init_grp_delete_netmask(ig, imasks[i]);
311 		if (rc != 0) {
312 			goto cleanup;
313 		}
314 	}
315 	return 0;
316 
317 cleanup:
318 	for (; i > 0; --i) {
319 		rc = spdk_iscsi_init_grp_add_netmask(ig, imasks[i - 1]);
320 		if (rc != 0) {
321 			spdk_iscsi_init_grp_delete_all_netmasks(ig);
322 			break;
323 		}
324 	}
325 	return -1;
326 }
327 
328 /* Read spdk iscsi target's config file and create initiator group */
329 static int
330 spdk_iscsi_parse_init_grp(struct spdk_conf_section *sp)
331 {
332 	int i, rc = 0;
333 	const char *val = NULL;
334 	int num_initiator_names;
335 	int num_initiator_masks;
336 	char **initiators = NULL, **netmasks = NULL;
337 	int tag = spdk_conf_section_get_num(sp);
338 
339 	SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "add initiator group %d\n", tag);
340 
341 	val = spdk_conf_section_get_val(sp, "Comment");
342 	if (val != NULL) {
343 		SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "Comment %s\n", val);
344 	}
345 
346 	/* counts number of definitions */
347 	for (i = 0; ; i++) {
348 		val = spdk_conf_section_get_nval(sp, "InitiatorName", i);
349 		if (val == NULL) {
350 			break;
351 		}
352 	}
353 	if (i == 0) {
354 		SPDK_ERRLOG("num_initiator_names = 0\n");
355 		return -EINVAL;
356 	}
357 	num_initiator_names = i;
358 	if (num_initiator_names > MAX_INITIATOR) {
359 		SPDK_ERRLOG("%d > MAX_INITIATOR\n", num_initiator_names);
360 		return -E2BIG;
361 	}
362 	for (i = 0; ; i++) {
363 		val = spdk_conf_section_get_nval(sp, "Netmask", i);
364 		if (val == NULL) {
365 			break;
366 		}
367 	}
368 	if (i == 0) {
369 		SPDK_ERRLOG("num_initiator_mask = 0\n");
370 		return -EINVAL;
371 	}
372 	num_initiator_masks = i;
373 	if (num_initiator_masks > MAX_NETMASK) {
374 		SPDK_ERRLOG("%d > MAX_NETMASK\n", num_initiator_masks);
375 		return -E2BIG;
376 	}
377 
378 	initiators = calloc(num_initiator_names, sizeof(char *));
379 	if (!initiators) {
380 		SPDK_ERRLOG("calloc() failed for temp initiator name array\n");
381 		return -ENOMEM;
382 	}
383 	for (i = 0; i < num_initiator_names; i++) {
384 		val = spdk_conf_section_get_nval(sp, "InitiatorName", i);
385 		if (!val) {
386 			SPDK_ERRLOG("InitiatorName %d not found\n", i);
387 			rc = -EINVAL;
388 			goto cleanup;
389 		}
390 		SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "InitiatorName %s\n", val);
391 		initiators[i] = strdup(val);
392 		if (!initiators[i]) {
393 			SPDK_ERRLOG("strdup() failed for temp initiator name\n");
394 			rc = -ENOMEM;
395 			goto cleanup;
396 		}
397 	}
398 	netmasks = calloc(num_initiator_masks, sizeof(char *));
399 	if (!netmasks) {
400 		SPDK_ERRLOG("malloc() failed for portal group\n");
401 		rc = -ENOMEM;
402 		goto cleanup;
403 	}
404 	for (i = 0; i < num_initiator_masks; i++) {
405 		val = spdk_conf_section_get_nval(sp, "Netmask", i);
406 		if (!val) {
407 			SPDK_ERRLOG("Netmask %d not found\n", i);
408 			rc = -EINVAL;
409 			goto cleanup;
410 		}
411 		SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "Netmask %s\n", val);
412 		netmasks[i] = strdup(val);
413 		if (!netmasks[i]) {
414 			SPDK_ERRLOG("strdup() failed for temp initiator mask\n");
415 			rc = -ENOMEM;
416 			goto cleanup;
417 		}
418 	}
419 
420 	rc = spdk_iscsi_init_grp_create_from_initiator_list(tag,
421 			num_initiator_names, initiators, num_initiator_masks, netmasks);
422 
423 cleanup:
424 	if (initiators) {
425 		for (i = 0; i < num_initiator_names; i++) {
426 			if (initiators[i]) {
427 				free(initiators[i]);
428 			}
429 		}
430 		free(initiators);
431 	}
432 	if (netmasks) {
433 		for (i = 0; i < num_initiator_masks; i++) {
434 			if (netmasks[i]) {
435 				free(netmasks[i]);
436 			}
437 		}
438 		free(netmasks);
439 	}
440 	return rc;
441 }
442 
443 int
444 spdk_iscsi_init_grp_register(struct spdk_iscsi_init_grp *ig)
445 {
446 	struct spdk_iscsi_init_grp *tmp;
447 	int rc = -1;
448 
449 	assert(ig != NULL);
450 
451 	pthread_mutex_lock(&g_spdk_iscsi.mutex);
452 	tmp = spdk_iscsi_init_grp_find_by_tag(ig->tag);
453 	if (tmp == NULL) {
454 		TAILQ_INSERT_TAIL(&g_spdk_iscsi.ig_head, ig, tailq);
455 		rc = 0;
456 	}
457 	pthread_mutex_unlock(&g_spdk_iscsi.mutex);
458 
459 	return rc;
460 }
461 
462 /*
463  * Create initiator group from list of initiator ip/hostnames and netmasks
464  * The initiator hostname/netmask lists are allocated by the caller on the
465  * heap.  Freed later by common initiator_group_destroy() code
466  */
467 int
468 spdk_iscsi_init_grp_create_from_initiator_list(int tag,
469 		int num_initiator_names,
470 		char **initiator_names,
471 		int num_initiator_masks,
472 		char **initiator_masks)
473 {
474 	int rc = -1;
475 	struct spdk_iscsi_init_grp *ig = NULL;
476 
477 	SPDK_DEBUGLOG(SPDK_LOG_ISCSI,
478 		      "add initiator group (from initiator list) tag=%d, #initiators=%d, #masks=%d\n",
479 		      tag, num_initiator_names, num_initiator_masks);
480 
481 	ig = spdk_iscsi_init_grp_create(tag);
482 	if (!ig) {
483 		SPDK_ERRLOG("initiator group create error (%d)\n", tag);
484 		return rc;
485 	}
486 
487 	rc = spdk_iscsi_init_grp_add_initiators(ig, num_initiator_names,
488 						initiator_names);
489 	if (rc < 0) {
490 		SPDK_ERRLOG("add initiator name error\n");
491 		goto cleanup;
492 	}
493 
494 	rc = spdk_iscsi_init_grp_add_netmasks(ig, num_initiator_masks,
495 					      initiator_masks);
496 	if (rc < 0) {
497 		SPDK_ERRLOG("add initiator netmask error\n");
498 		goto cleanup;
499 	}
500 
501 	rc = spdk_iscsi_init_grp_register(ig);
502 	if (rc < 0) {
503 		SPDK_ERRLOG("initiator group register error (%d)\n", tag);
504 		goto cleanup;
505 	}
506 	return 0;
507 
508 cleanup:
509 	spdk_iscsi_init_grp_destroy(ig);
510 	return rc;
511 }
512 
513 int
514 spdk_iscsi_init_grp_add_initiators_from_initiator_list(int tag,
515 		int num_initiator_names,
516 		char **initiator_names,
517 		int num_initiator_masks,
518 		char **initiator_masks)
519 {
520 	int rc = -1;
521 	struct spdk_iscsi_init_grp *ig;
522 
523 	SPDK_DEBUGLOG(SPDK_LOG_ISCSI,
524 		      "add initiator to initiator group: tag=%d, #initiators=%d, #masks=%d\n",
525 		      tag, num_initiator_names, num_initiator_masks);
526 
527 	pthread_mutex_lock(&g_spdk_iscsi.mutex);
528 	ig = spdk_iscsi_init_grp_find_by_tag(tag);
529 	if (!ig) {
530 		pthread_mutex_unlock(&g_spdk_iscsi.mutex);
531 		SPDK_ERRLOG("initiator group (%d) is not found\n", tag);
532 		return rc;
533 	}
534 
535 	rc = spdk_iscsi_init_grp_add_initiators(ig, num_initiator_names,
536 						initiator_names);
537 	if (rc < 0) {
538 		SPDK_ERRLOG("add initiator name error\n");
539 		goto error;
540 	}
541 
542 	rc = spdk_iscsi_init_grp_add_netmasks(ig, num_initiator_masks,
543 					      initiator_masks);
544 	if (rc < 0) {
545 		SPDK_ERRLOG("add initiator netmask error\n");
546 		spdk_iscsi_init_grp_delete_initiators(ig, num_initiator_names,
547 						      initiator_names);
548 	}
549 
550 error:
551 	pthread_mutex_unlock(&g_spdk_iscsi.mutex);
552 	return rc;
553 }
554 
555 int
556 spdk_iscsi_init_grp_delete_initiators_from_initiator_list(int tag,
557 		int num_initiator_names,
558 		char **initiator_names,
559 		int num_initiator_masks,
560 		char **initiator_masks)
561 {
562 	int rc = -1;
563 	struct spdk_iscsi_init_grp *ig;
564 
565 	SPDK_DEBUGLOG(SPDK_LOG_ISCSI,
566 		      "delete initiator from initiator group: tag=%d, #initiators=%d, #masks=%d\n",
567 		      tag, num_initiator_names, num_initiator_masks);
568 
569 	pthread_mutex_lock(&g_spdk_iscsi.mutex);
570 	ig = spdk_iscsi_init_grp_find_by_tag(tag);
571 	if (!ig) {
572 		pthread_mutex_unlock(&g_spdk_iscsi.mutex);
573 		SPDK_ERRLOG("initiator group (%d) is not found\n", tag);
574 		return rc;
575 	}
576 
577 	rc = spdk_iscsi_init_grp_delete_initiators(ig, num_initiator_names,
578 			initiator_names);
579 	if (rc < 0) {
580 		SPDK_ERRLOG("delete initiator name error\n");
581 		goto error;
582 	}
583 
584 	rc = spdk_iscsi_init_grp_delete_netmasks(ig, num_initiator_masks,
585 			initiator_masks);
586 	if (rc < 0) {
587 		SPDK_ERRLOG("delete initiator netmask error\n");
588 		spdk_iscsi_init_grp_add_initiators(ig, num_initiator_names,
589 						   initiator_names);
590 		goto error;
591 	}
592 
593 error:
594 	pthread_mutex_unlock(&g_spdk_iscsi.mutex);
595 	return rc;
596 }
597 
598 void
599 spdk_iscsi_init_grp_destroy(struct spdk_iscsi_init_grp *ig)
600 {
601 	if (!ig) {
602 		return;
603 	}
604 
605 	spdk_iscsi_init_grp_delete_all_initiators(ig);
606 	spdk_iscsi_init_grp_delete_all_netmasks(ig);
607 	free(ig);
608 };
609 
610 struct spdk_iscsi_init_grp *
611 spdk_iscsi_init_grp_find_by_tag(int tag)
612 {
613 	struct spdk_iscsi_init_grp *ig;
614 
615 	TAILQ_FOREACH(ig, &g_spdk_iscsi.ig_head, tailq) {
616 		if (ig->tag == tag) {
617 			return ig;
618 		}
619 	}
620 
621 	return NULL;
622 }
623 
624 int
625 spdk_iscsi_parse_init_grps(void)
626 {
627 	struct spdk_conf_section *sp;
628 	int rc;
629 
630 	sp = spdk_conf_first_section(NULL);
631 	while (sp != NULL) {
632 		if (spdk_conf_section_match_prefix(sp, "InitiatorGroup")) {
633 			if (spdk_conf_section_get_num(sp) == 0) {
634 				SPDK_ERRLOG("Group 0 is invalid\n");
635 				return -1;
636 			}
637 			rc = spdk_iscsi_parse_init_grp(sp);
638 			if (rc < 0) {
639 				SPDK_ERRLOG("parse_init_group() failed\n");
640 				return -1;
641 			}
642 		}
643 		sp = spdk_conf_next_section(sp);
644 	}
645 	return 0;
646 }
647 
648 void
649 spdk_iscsi_init_grps_destroy(void)
650 {
651 	struct spdk_iscsi_init_grp *ig, *tmp;
652 
653 	SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "spdk_iscsi_init_grp_array_destroy\n");
654 	pthread_mutex_lock(&g_spdk_iscsi.mutex);
655 	TAILQ_FOREACH_SAFE(ig, &g_spdk_iscsi.ig_head, tailq, tmp) {
656 		TAILQ_REMOVE(&g_spdk_iscsi.ig_head, ig, tailq);
657 		spdk_iscsi_init_grp_destroy(ig);
658 	}
659 	pthread_mutex_unlock(&g_spdk_iscsi.mutex);
660 }
661 
662 struct spdk_iscsi_init_grp *
663 spdk_iscsi_init_grp_unregister(int tag)
664 {
665 	struct spdk_iscsi_init_grp *ig;
666 
667 	pthread_mutex_lock(&g_spdk_iscsi.mutex);
668 	TAILQ_FOREACH(ig, &g_spdk_iscsi.ig_head, tailq) {
669 		if (ig->tag == tag) {
670 			TAILQ_REMOVE(&g_spdk_iscsi.ig_head, ig, tailq);
671 			pthread_mutex_unlock(&g_spdk_iscsi.mutex);
672 			return ig;
673 		}
674 	}
675 	pthread_mutex_unlock(&g_spdk_iscsi.mutex);
676 	return NULL;
677 }
678 
679 static const char *initiator_group_section = \
680 		"\n"
681 		"# Users must change the InitiatorGroup section(s) to match the IP\n"
682 		"#  addresses and initiator configuration in their environment.\n"
683 		"# Netmask can be used to specify a single IP address or a range of IP addresses\n"
684 		"#  Netmask 192.168.1.20   <== single IP address\n"
685 		"#  Netmask 192.168.1.0/24 <== IP range 192.168.1.*\n";
686 
687 #define INITIATOR_GROUP_TMPL \
688 "[InitiatorGroup%d]\n" \
689 "  Comment \"Initiator Group%d\"\n"
690 
691 #define INITIATOR_TMPL \
692 "  InitiatorName "
693 
694 #define NETMASK_TMPL \
695 "  Netmask "
696 
697 void
698 spdk_iscsi_init_grps_config_text(FILE *fp)
699 {
700 	struct spdk_iscsi_init_grp *ig;
701 	struct spdk_iscsi_initiator_name *iname;
702 	struct spdk_iscsi_initiator_netmask *imask;
703 
704 	/* Create initiator group section */
705 	fprintf(fp, "%s", initiator_group_section);
706 
707 	/* Dump initiator groups */
708 	TAILQ_FOREACH(ig, &g_spdk_iscsi.ig_head, tailq) {
709 		if (NULL == ig) { continue; }
710 		fprintf(fp, INITIATOR_GROUP_TMPL, ig->tag, ig->tag);
711 
712 		/* Dump initiators */
713 		fprintf(fp, INITIATOR_TMPL);
714 		TAILQ_FOREACH(iname, &ig->initiator_head, tailq) {
715 			fprintf(fp, "%s ", iname->name);
716 		}
717 		fprintf(fp, "\n");
718 
719 		/* Dump netmasks */
720 		fprintf(fp, NETMASK_TMPL);
721 		TAILQ_FOREACH(imask, &ig->netmask_head, tailq) {
722 			fprintf(fp, "%s ", imask->mask);
723 		}
724 		fprintf(fp, "\n");
725 	}
726 }
727 
728 static void
729 spdk_iscsi_init_grp_info_json(struct spdk_iscsi_init_grp *ig,
730 			      struct spdk_json_write_ctx *w)
731 {
732 	struct spdk_iscsi_initiator_name *iname;
733 	struct spdk_iscsi_initiator_netmask *imask;
734 
735 	spdk_json_write_object_begin(w);
736 
737 	spdk_json_write_named_int32(w, "tag", ig->tag);
738 
739 	spdk_json_write_named_array_begin(w, "initiators");
740 	TAILQ_FOREACH(iname, &ig->initiator_head, tailq) {
741 		spdk_json_write_string(w, iname->name);
742 	}
743 	spdk_json_write_array_end(w);
744 
745 	spdk_json_write_named_array_begin(w, "netmasks");
746 	TAILQ_FOREACH(imask, &ig->netmask_head, tailq) {
747 		spdk_json_write_string(w, imask->mask);
748 	}
749 	spdk_json_write_array_end(w);
750 
751 	spdk_json_write_object_end(w);
752 }
753 
754 static void
755 spdk_iscsi_init_grp_config_json(struct spdk_iscsi_init_grp *ig,
756 				struct spdk_json_write_ctx *w)
757 {
758 	spdk_json_write_object_begin(w);
759 
760 	spdk_json_write_named_string(w, "method", "add_initiator_group");
761 
762 	spdk_json_write_name(w, "params");
763 	spdk_iscsi_init_grp_info_json(ig, w);
764 
765 	spdk_json_write_object_end(w);
766 }
767 
768 void
769 spdk_iscsi_init_grps_info_json(struct spdk_json_write_ctx *w)
770 {
771 	struct spdk_iscsi_init_grp *ig;
772 
773 	TAILQ_FOREACH(ig, &g_spdk_iscsi.ig_head, tailq) {
774 		spdk_iscsi_init_grp_info_json(ig, w);
775 	}
776 }
777 
778 void
779 spdk_iscsi_init_grps_config_json(struct spdk_json_write_ctx *w)
780 {
781 	struct spdk_iscsi_init_grp *ig;
782 
783 	TAILQ_FOREACH(ig, &g_spdk_iscsi.ig_head, tailq) {
784 		spdk_iscsi_init_grp_config_json(ig, w);
785 	}
786 }
787