xref: /spdk/lib/iscsi/init_grp.c (revision 4e8e97c886e47e337dc470ac8c1ffa044d729af0)
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/log.h"
41 
42 #include "iscsi/iscsi.h"
43 #include "iscsi/init_grp.h"
44 
45 static struct spdk_iscsi_init_grp *
46 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 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 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 	size_t len;
81 
82 	if (ig->ninitiators >= MAX_INITIATOR) {
83 		SPDK_ERRLOG("> MAX_INITIATOR(=%d) is not allowed\n", MAX_INITIATOR);
84 		return -EPERM;
85 	}
86 
87 	len = strlen(name);
88 	if (len > MAX_INITIATOR_NAME) {
89 		SPDK_ERRLOG("Initiator Name is larger than 223 bytes\n");
90 		return -EINVAL;
91 	}
92 
93 	iname = iscsi_init_grp_find_initiator(ig, name);
94 	if (iname != NULL) {
95 		return -EEXIST;
96 	}
97 
98 	iname = calloc(1, sizeof(*iname));
99 	if (iname == NULL) {
100 		SPDK_ERRLOG("malloc() failed for initiator name str\n");
101 		return -ENOMEM;
102 	}
103 
104 	memcpy(iname->name, name, len);
105 
106 	/* Replace "ALL" by "ANY" if set */
107 	p = strstr(iname->name, "ALL");
108 	if (p != NULL) {
109 		SPDK_WARNLOG("Please use \"%s\" instead of \"%s\"\n", "ANY", "ALL");
110 		SPDK_WARNLOG("Converting \"%s\" to \"%s\" automatically\n", "ALL", "ANY");
111 		memcpy(p, "ANY", 3);
112 	}
113 
114 	TAILQ_INSERT_TAIL(&ig->initiator_head, iname, tailq);
115 	ig->ninitiators++;
116 
117 	SPDK_DEBUGLOG(iscsi, "InitiatorName %s\n", name);
118 	return 0;
119 }
120 
121 static int
122 iscsi_init_grp_delete_initiator(struct spdk_iscsi_init_grp *ig, char *name)
123 {
124 	struct spdk_iscsi_initiator_name *iname;
125 
126 	iname = iscsi_init_grp_find_initiator(ig, name);
127 	if (iname == NULL) {
128 		return -ENOENT;
129 	}
130 
131 	TAILQ_REMOVE(&ig->initiator_head, iname, tailq);
132 	ig->ninitiators--;
133 	free(iname);
134 	return 0;
135 }
136 
137 static int
138 iscsi_init_grp_add_initiators(struct spdk_iscsi_init_grp *ig, int num_inames,
139 			      char **inames)
140 {
141 	int i;
142 	int rc;
143 
144 	for (i = 0; i < num_inames; i++) {
145 		rc = iscsi_init_grp_add_initiator(ig, inames[i]);
146 		if (rc < 0) {
147 			goto cleanup;
148 		}
149 	}
150 	return 0;
151 
152 cleanup:
153 	for (; i > 0; --i) {
154 		iscsi_init_grp_delete_initiator(ig, inames[i - 1]);
155 	}
156 	return rc;
157 }
158 
159 static void
160 iscsi_init_grp_delete_all_initiators(struct spdk_iscsi_init_grp *ig)
161 {
162 	struct spdk_iscsi_initiator_name *iname, *tmp;
163 
164 	TAILQ_FOREACH_SAFE(iname, &ig->initiator_head, tailq, tmp) {
165 		TAILQ_REMOVE(&ig->initiator_head, iname, tailq);
166 		ig->ninitiators--;
167 		free(iname);
168 	}
169 }
170 
171 static int
172 iscsi_init_grp_delete_initiators(struct spdk_iscsi_init_grp *ig, int num_inames, char **inames)
173 {
174 	int i;
175 	int rc;
176 
177 	for (i = 0; i < num_inames; i++) {
178 		rc = iscsi_init_grp_delete_initiator(ig, inames[i]);
179 		if (rc < 0) {
180 			goto cleanup;
181 		}
182 	}
183 	return 0;
184 
185 cleanup:
186 	for (; i > 0; --i) {
187 		rc = iscsi_init_grp_add_initiator(ig, inames[i - 1]);
188 		if (rc != 0) {
189 			iscsi_init_grp_delete_all_initiators(ig);
190 			break;
191 		}
192 	}
193 	return -1;
194 }
195 
196 static struct spdk_iscsi_initiator_netmask *
197 iscsi_init_grp_find_netmask(struct spdk_iscsi_init_grp *ig, const char *mask)
198 {
199 	struct spdk_iscsi_initiator_netmask *netmask;
200 
201 	TAILQ_FOREACH(netmask, &ig->netmask_head, tailq) {
202 		if (!strcmp(netmask->mask, mask)) {
203 			return netmask;
204 		}
205 	}
206 	return NULL;
207 }
208 
209 static int
210 iscsi_init_grp_add_netmask(struct spdk_iscsi_init_grp *ig, char *mask)
211 {
212 	struct spdk_iscsi_initiator_netmask *imask;
213 	char *p;
214 	size_t len;
215 
216 	if (ig->nnetmasks >= MAX_NETMASK) {
217 		SPDK_ERRLOG("> MAX_NETMASK(=%d) is not allowed\n", MAX_NETMASK);
218 		return -EPERM;
219 	}
220 
221 	len = strlen(mask);
222 	if (len > MAX_INITIATOR_ADDR) {
223 		SPDK_ERRLOG("Initiator Name is larger than %d bytes\n", MAX_INITIATOR_ADDR);
224 		return -EINVAL;
225 	}
226 
227 	imask = iscsi_init_grp_find_netmask(ig, mask);
228 	if (imask != NULL) {
229 		return -EEXIST;
230 	}
231 
232 	imask = calloc(1, sizeof(*imask));
233 	if (imask == NULL) {
234 		SPDK_ERRLOG("malloc() failed for inititator mask str\n");
235 		return -ENOMEM;
236 	}
237 
238 	memcpy(imask->mask, mask, len);
239 
240 	/* Replace "ALL" by "ANY" if set */
241 	p = strstr(imask->mask, "ALL");
242 	if (p != NULL) {
243 		SPDK_WARNLOG("Please use \"%s\" instead of \"%s\"\n", "ANY", "ALL");
244 		SPDK_WARNLOG("Converting \"%s\" to \"%s\" automatically\n", "ALL", "ANY");
245 		memcpy(p, "ANY", 3);
246 	}
247 
248 	TAILQ_INSERT_TAIL(&ig->netmask_head, imask, tailq);
249 	ig->nnetmasks++;
250 
251 	SPDK_DEBUGLOG(iscsi, "Netmask %s\n", mask);
252 	return 0;
253 }
254 
255 static int
256 iscsi_init_grp_delete_netmask(struct spdk_iscsi_init_grp *ig, char *mask)
257 {
258 	struct spdk_iscsi_initiator_netmask *imask;
259 
260 	imask = iscsi_init_grp_find_netmask(ig, mask);
261 	if (imask == NULL) {
262 		return -ENOENT;
263 	}
264 
265 	TAILQ_REMOVE(&ig->netmask_head, imask, tailq);
266 	ig->nnetmasks--;
267 	free(imask);
268 	return 0;
269 }
270 
271 static int
272 iscsi_init_grp_add_netmasks(struct spdk_iscsi_init_grp *ig, int num_imasks, char **imasks)
273 {
274 	int i;
275 	int rc;
276 
277 	for (i = 0; i < num_imasks; i++) {
278 		rc = iscsi_init_grp_add_netmask(ig, imasks[i]);
279 		if (rc != 0) {
280 			goto cleanup;
281 		}
282 	}
283 	return 0;
284 
285 cleanup:
286 	for (; i > 0; --i) {
287 		iscsi_init_grp_delete_netmask(ig, imasks[i - 1]);
288 	}
289 	return rc;
290 }
291 
292 static void
293 iscsi_init_grp_delete_all_netmasks(struct spdk_iscsi_init_grp *ig)
294 {
295 	struct spdk_iscsi_initiator_netmask *imask, *tmp;
296 
297 	TAILQ_FOREACH_SAFE(imask, &ig->netmask_head, tailq, tmp) {
298 		TAILQ_REMOVE(&ig->netmask_head, imask, tailq);
299 		ig->nnetmasks--;
300 		free(imask);
301 	}
302 }
303 
304 static int
305 iscsi_init_grp_delete_netmasks(struct spdk_iscsi_init_grp *ig, int num_imasks, char **imasks)
306 {
307 	int i;
308 	int rc;
309 
310 	for (i = 0; i < num_imasks; i++) {
311 		rc = iscsi_init_grp_delete_netmask(ig, imasks[i]);
312 		if (rc != 0) {
313 			goto cleanup;
314 		}
315 	}
316 	return 0;
317 
318 cleanup:
319 	for (; i > 0; --i) {
320 		rc = iscsi_init_grp_add_netmask(ig, imasks[i - 1]);
321 		if (rc != 0) {
322 			iscsi_init_grp_delete_all_netmasks(ig);
323 			break;
324 		}
325 	}
326 	return -1;
327 }
328 
329 /* Read spdk iscsi target's config file and create initiator group */
330 static int
331 iscsi_parse_init_grp(struct spdk_conf_section *sp)
332 {
333 	int i, rc = 0;
334 	const char *val = NULL;
335 	int num_initiator_names;
336 	int num_initiator_masks;
337 	char **initiators = NULL, **netmasks = NULL;
338 	int tag = spdk_conf_section_get_num(sp);
339 
340 	SPDK_DEBUGLOG(iscsi, "add initiator group %d\n", tag);
341 
342 	val = spdk_conf_section_get_val(sp, "Comment");
343 	if (val != NULL) {
344 		SPDK_DEBUGLOG(iscsi, "Comment %s\n", val);
345 	}
346 
347 	/* counts number of definitions */
348 	for (i = 0; ; i++) {
349 		val = spdk_conf_section_get_nval(sp, "InitiatorName", i);
350 		if (val == NULL) {
351 			break;
352 		}
353 	}
354 	if (i == 0) {
355 		SPDK_ERRLOG("num_initiator_names = 0\n");
356 		return -EINVAL;
357 	}
358 	num_initiator_names = i;
359 	if (num_initiator_names > MAX_INITIATOR) {
360 		SPDK_ERRLOG("%d > MAX_INITIATOR\n", num_initiator_names);
361 		return -E2BIG;
362 	}
363 	for (i = 0; ; i++) {
364 		val = spdk_conf_section_get_nval(sp, "Netmask", i);
365 		if (val == NULL) {
366 			break;
367 		}
368 	}
369 	if (i == 0) {
370 		SPDK_ERRLOG("num_initiator_mask = 0\n");
371 		return -EINVAL;
372 	}
373 	num_initiator_masks = i;
374 	if (num_initiator_masks > MAX_NETMASK) {
375 		SPDK_ERRLOG("%d > MAX_NETMASK\n", num_initiator_masks);
376 		return -E2BIG;
377 	}
378 
379 	initiators = calloc(num_initiator_names, sizeof(char *));
380 	if (!initiators) {
381 		SPDK_ERRLOG("calloc() failed for temp initiator name array\n");
382 		return -ENOMEM;
383 	}
384 	for (i = 0; i < num_initiator_names; i++) {
385 		val = spdk_conf_section_get_nval(sp, "InitiatorName", i);
386 		if (!val) {
387 			SPDK_ERRLOG("InitiatorName %d not found\n", i);
388 			rc = -EINVAL;
389 			goto cleanup;
390 		}
391 		SPDK_DEBUGLOG(iscsi, "InitiatorName %s\n", val);
392 		initiators[i] = strdup(val);
393 		if (!initiators[i]) {
394 			SPDK_ERRLOG("strdup() failed for temp initiator name\n");
395 			rc = -ENOMEM;
396 			goto cleanup;
397 		}
398 	}
399 	netmasks = calloc(num_initiator_masks, sizeof(char *));
400 	if (!netmasks) {
401 		SPDK_ERRLOG("malloc() failed for portal group\n");
402 		rc = -ENOMEM;
403 		goto cleanup;
404 	}
405 	for (i = 0; i < num_initiator_masks; i++) {
406 		val = spdk_conf_section_get_nval(sp, "Netmask", i);
407 		if (!val) {
408 			SPDK_ERRLOG("Netmask %d not found\n", i);
409 			rc = -EINVAL;
410 			goto cleanup;
411 		}
412 		SPDK_DEBUGLOG(iscsi, "Netmask %s\n", val);
413 		netmasks[i] = strdup(val);
414 		if (!netmasks[i]) {
415 			SPDK_ERRLOG("strdup() failed for temp initiator mask\n");
416 			rc = -ENOMEM;
417 			goto cleanup;
418 		}
419 	}
420 
421 	rc = iscsi_init_grp_create_from_initiator_list(tag,
422 			num_initiator_names, initiators, num_initiator_masks, netmasks);
423 
424 cleanup:
425 	if (initiators) {
426 		for (i = 0; i < num_initiator_names; i++) {
427 			if (initiators[i]) {
428 				free(initiators[i]);
429 			}
430 		}
431 		free(initiators);
432 	}
433 	if (netmasks) {
434 		for (i = 0; i < num_initiator_masks; i++) {
435 			if (netmasks[i]) {
436 				free(netmasks[i]);
437 			}
438 		}
439 		free(netmasks);
440 	}
441 	return rc;
442 }
443 
444 int
445 iscsi_init_grp_register(struct spdk_iscsi_init_grp *ig)
446 {
447 	struct spdk_iscsi_init_grp *tmp;
448 	int rc = -1;
449 
450 	assert(ig != NULL);
451 
452 	pthread_mutex_lock(&g_iscsi.mutex);
453 	tmp = iscsi_init_grp_find_by_tag(ig->tag);
454 	if (tmp == NULL) {
455 		TAILQ_INSERT_TAIL(&g_iscsi.ig_head, ig, tailq);
456 		rc = 0;
457 	}
458 	pthread_mutex_unlock(&g_iscsi.mutex);
459 
460 	return rc;
461 }
462 
463 /*
464  * Create initiator group from list of initiator ip/hostnames and netmasks
465  * The initiator hostname/netmask lists are allocated by the caller on the
466  * heap.  Freed later by common initiator_group_destroy() code
467  */
468 int
469 iscsi_init_grp_create_from_initiator_list(int tag,
470 		int num_initiator_names,
471 		char **initiator_names,
472 		int num_initiator_masks,
473 		char **initiator_masks)
474 {
475 	int rc = -1;
476 	struct spdk_iscsi_init_grp *ig = NULL;
477 
478 	SPDK_DEBUGLOG(iscsi,
479 		      "add initiator group (from initiator list) tag=%d, #initiators=%d, #masks=%d\n",
480 		      tag, num_initiator_names, num_initiator_masks);
481 
482 	ig = iscsi_init_grp_create(tag);
483 	if (!ig) {
484 		SPDK_ERRLOG("initiator group create error (%d)\n", tag);
485 		return rc;
486 	}
487 
488 	rc = iscsi_init_grp_add_initiators(ig, num_initiator_names,
489 					   initiator_names);
490 	if (rc < 0) {
491 		SPDK_ERRLOG("add initiator name error\n");
492 		goto cleanup;
493 	}
494 
495 	rc = iscsi_init_grp_add_netmasks(ig, num_initiator_masks,
496 					 initiator_masks);
497 	if (rc < 0) {
498 		SPDK_ERRLOG("add initiator netmask error\n");
499 		goto cleanup;
500 	}
501 
502 	rc = iscsi_init_grp_register(ig);
503 	if (rc < 0) {
504 		SPDK_ERRLOG("initiator group register error (%d)\n", tag);
505 		goto cleanup;
506 	}
507 	return 0;
508 
509 cleanup:
510 	iscsi_init_grp_destroy(ig);
511 	return rc;
512 }
513 
514 int
515 iscsi_init_grp_add_initiators_from_initiator_list(int tag,
516 		int num_initiator_names,
517 		char **initiator_names,
518 		int num_initiator_masks,
519 		char **initiator_masks)
520 {
521 	int rc = -1;
522 	struct spdk_iscsi_init_grp *ig;
523 
524 	SPDK_DEBUGLOG(iscsi,
525 		      "add initiator to initiator group: tag=%d, #initiators=%d, #masks=%d\n",
526 		      tag, num_initiator_names, num_initiator_masks);
527 
528 	pthread_mutex_lock(&g_iscsi.mutex);
529 	ig = iscsi_init_grp_find_by_tag(tag);
530 	if (!ig) {
531 		pthread_mutex_unlock(&g_iscsi.mutex);
532 		SPDK_ERRLOG("initiator group (%d) is not found\n", tag);
533 		return rc;
534 	}
535 
536 	rc = iscsi_init_grp_add_initiators(ig, num_initiator_names,
537 					   initiator_names);
538 	if (rc < 0) {
539 		SPDK_ERRLOG("add initiator name error\n");
540 		goto error;
541 	}
542 
543 	rc = iscsi_init_grp_add_netmasks(ig, num_initiator_masks,
544 					 initiator_masks);
545 	if (rc < 0) {
546 		SPDK_ERRLOG("add initiator netmask error\n");
547 		iscsi_init_grp_delete_initiators(ig, num_initiator_names,
548 						 initiator_names);
549 	}
550 
551 error:
552 	pthread_mutex_unlock(&g_iscsi.mutex);
553 	return rc;
554 }
555 
556 int
557 iscsi_init_grp_delete_initiators_from_initiator_list(int tag,
558 		int num_initiator_names,
559 		char **initiator_names,
560 		int num_initiator_masks,
561 		char **initiator_masks)
562 {
563 	int rc = -1;
564 	struct spdk_iscsi_init_grp *ig;
565 
566 	SPDK_DEBUGLOG(iscsi,
567 		      "delete initiator from initiator group: tag=%d, #initiators=%d, #masks=%d\n",
568 		      tag, num_initiator_names, num_initiator_masks);
569 
570 	pthread_mutex_lock(&g_iscsi.mutex);
571 	ig = iscsi_init_grp_find_by_tag(tag);
572 	if (!ig) {
573 		pthread_mutex_unlock(&g_iscsi.mutex);
574 		SPDK_ERRLOG("initiator group (%d) is not found\n", tag);
575 		return rc;
576 	}
577 
578 	rc = iscsi_init_grp_delete_initiators(ig, num_initiator_names,
579 					      initiator_names);
580 	if (rc < 0) {
581 		SPDK_ERRLOG("delete initiator name error\n");
582 		goto error;
583 	}
584 
585 	rc = iscsi_init_grp_delete_netmasks(ig, num_initiator_masks,
586 					    initiator_masks);
587 	if (rc < 0) {
588 		SPDK_ERRLOG("delete initiator netmask error\n");
589 		iscsi_init_grp_add_initiators(ig, num_initiator_names,
590 					      initiator_names);
591 		goto error;
592 	}
593 
594 error:
595 	pthread_mutex_unlock(&g_iscsi.mutex);
596 	return rc;
597 }
598 
599 void
600 iscsi_init_grp_destroy(struct spdk_iscsi_init_grp *ig)
601 {
602 	if (!ig) {
603 		return;
604 	}
605 
606 	iscsi_init_grp_delete_all_initiators(ig);
607 	iscsi_init_grp_delete_all_netmasks(ig);
608 	free(ig);
609 };
610 
611 struct spdk_iscsi_init_grp *
612 iscsi_init_grp_find_by_tag(int tag)
613 {
614 	struct spdk_iscsi_init_grp *ig;
615 
616 	TAILQ_FOREACH(ig, &g_iscsi.ig_head, tailq) {
617 		if (ig->tag == tag) {
618 			return ig;
619 		}
620 	}
621 
622 	return NULL;
623 }
624 
625 int
626 iscsi_parse_init_grps(void)
627 {
628 	struct spdk_conf_section *sp;
629 	int rc;
630 
631 	sp = spdk_conf_first_section(NULL);
632 	while (sp != NULL) {
633 		if (spdk_conf_section_match_prefix(sp, "InitiatorGroup")) {
634 			if (spdk_conf_section_get_num(sp) == 0) {
635 				SPDK_ERRLOG("Group 0 is invalid\n");
636 				return -1;
637 			}
638 			rc = iscsi_parse_init_grp(sp);
639 			if (rc < 0) {
640 				SPDK_ERRLOG("parse_init_group() failed\n");
641 				return -1;
642 			}
643 		}
644 		sp = spdk_conf_next_section(sp);
645 	}
646 	return 0;
647 }
648 
649 void
650 iscsi_init_grps_destroy(void)
651 {
652 	struct spdk_iscsi_init_grp *ig, *tmp;
653 
654 	SPDK_DEBUGLOG(iscsi, "iscsi_init_grp_array_destroy\n");
655 	pthread_mutex_lock(&g_iscsi.mutex);
656 	TAILQ_FOREACH_SAFE(ig, &g_iscsi.ig_head, tailq, tmp) {
657 		TAILQ_REMOVE(&g_iscsi.ig_head, ig, tailq);
658 		iscsi_init_grp_destroy(ig);
659 	}
660 	pthread_mutex_unlock(&g_iscsi.mutex);
661 }
662 
663 struct spdk_iscsi_init_grp *
664 iscsi_init_grp_unregister(int tag)
665 {
666 	struct spdk_iscsi_init_grp *ig;
667 
668 	pthread_mutex_lock(&g_iscsi.mutex);
669 	TAILQ_FOREACH(ig, &g_iscsi.ig_head, tailq) {
670 		if (ig->tag == tag) {
671 			TAILQ_REMOVE(&g_iscsi.ig_head, ig, tailq);
672 			pthread_mutex_unlock(&g_iscsi.mutex);
673 			return ig;
674 		}
675 	}
676 	pthread_mutex_unlock(&g_iscsi.mutex);
677 	return NULL;
678 }
679 
680 static const char *initiator_group_section = \
681 		"\n"
682 		"# Users must change the InitiatorGroup section(s) to match the IP\n"
683 		"#  addresses and initiator configuration in their environment.\n"
684 		"# Netmask can be used to specify a single IP address or a range of IP addresses\n"
685 		"#  Netmask 192.168.1.20   <== single IP address\n"
686 		"#  Netmask 192.168.1.0/24 <== IP range 192.168.1.*\n";
687 
688 #define INITIATOR_GROUP_TMPL \
689 "[InitiatorGroup%d]\n" \
690 "  Comment \"Initiator Group%d\"\n"
691 
692 #define INITIATOR_TMPL \
693 "  InitiatorName "
694 
695 #define NETMASK_TMPL \
696 "  Netmask "
697 
698 void
699 iscsi_init_grps_config_text(FILE *fp)
700 {
701 	struct spdk_iscsi_init_grp *ig;
702 	struct spdk_iscsi_initiator_name *iname;
703 	struct spdk_iscsi_initiator_netmask *imask;
704 
705 	/* Create initiator group section */
706 	fprintf(fp, "%s", initiator_group_section);
707 
708 	/* Dump initiator groups */
709 	TAILQ_FOREACH(ig, &g_iscsi.ig_head, tailq) {
710 		if (NULL == ig) { continue; }
711 		fprintf(fp, INITIATOR_GROUP_TMPL, ig->tag, ig->tag);
712 
713 		/* Dump initiators */
714 		fprintf(fp, INITIATOR_TMPL);
715 		TAILQ_FOREACH(iname, &ig->initiator_head, tailq) {
716 			fprintf(fp, "%s ", iname->name);
717 		}
718 		fprintf(fp, "\n");
719 
720 		/* Dump netmasks */
721 		fprintf(fp, NETMASK_TMPL);
722 		TAILQ_FOREACH(imask, &ig->netmask_head, tailq) {
723 			fprintf(fp, "%s ", imask->mask);
724 		}
725 		fprintf(fp, "\n");
726 	}
727 }
728 
729 static void
730 iscsi_init_grp_info_json(struct spdk_iscsi_init_grp *ig,
731 			 struct spdk_json_write_ctx *w)
732 {
733 	struct spdk_iscsi_initiator_name *iname;
734 	struct spdk_iscsi_initiator_netmask *imask;
735 
736 	spdk_json_write_object_begin(w);
737 
738 	spdk_json_write_named_int32(w, "tag", ig->tag);
739 
740 	spdk_json_write_named_array_begin(w, "initiators");
741 	TAILQ_FOREACH(iname, &ig->initiator_head, tailq) {
742 		spdk_json_write_string(w, iname->name);
743 	}
744 	spdk_json_write_array_end(w);
745 
746 	spdk_json_write_named_array_begin(w, "netmasks");
747 	TAILQ_FOREACH(imask, &ig->netmask_head, tailq) {
748 		spdk_json_write_string(w, imask->mask);
749 	}
750 	spdk_json_write_array_end(w);
751 
752 	spdk_json_write_object_end(w);
753 }
754 
755 static void
756 iscsi_init_grp_config_json(struct spdk_iscsi_init_grp *ig,
757 			   struct spdk_json_write_ctx *w)
758 {
759 	spdk_json_write_object_begin(w);
760 
761 	spdk_json_write_named_string(w, "method", "iscsi_create_initiator_group");
762 
763 	spdk_json_write_name(w, "params");
764 	iscsi_init_grp_info_json(ig, w);
765 
766 	spdk_json_write_object_end(w);
767 }
768 
769 void
770 iscsi_init_grps_info_json(struct spdk_json_write_ctx *w)
771 {
772 	struct spdk_iscsi_init_grp *ig;
773 
774 	TAILQ_FOREACH(ig, &g_iscsi.ig_head, tailq) {
775 		iscsi_init_grp_info_json(ig, w);
776 	}
777 }
778 
779 void
780 iscsi_init_grps_config_json(struct spdk_json_write_ctx *w)
781 {
782 	struct spdk_iscsi_init_grp *ig;
783 
784 	TAILQ_FOREACH(ig, &g_iscsi.ig_head, tailq) {
785 		iscsi_init_grp_config_json(ig, w);
786 	}
787 }
788