xref: /netbsd-src/sbin/iscsid/iscsid_lists.c (revision 9ddb6ab554e70fb9bbd90c3d96b812bc57755a14)
1 /*	$NetBSD: iscsid_lists.c,v 1.3 2011/11/20 01:23:57 agc Exp $	*/
2 
3 /*-
4  * Copyright (c) 2005,2006,2011 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Wasabi Systems, Inc.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 
33 #include "iscsid_globals.h"
34 
35 /* counter for initiator ID */
36 static uint32_t initiator_id = 0;
37 
38 /* -------------------------------------------------------------------------- */
39 
40 /*#ifdef ISCSI_NOTHREAD */
41 #if 0
42 
43 /*
44  * verify_session:
45  *    Verify that a specific session still exists, delete it if not.
46  *
47  * Parameter:  The session pointer.
48  */
49 
50 static void
51 verify_session(session_t * sess)
52 {
53 	generic_entry_t *curr, *next;
54 	int nosess = 0;
55 
56 	for (curr = sess->connections.tqh_first; curr != NULL && !nosess; curr = next) {
57 		next = curr->link.tqe_next;
58 		nosess = verify_connection((connection_t *) curr) == ISCSI_STATUS_INVALID_SESSION_ID;
59 	}
60 
61 	if (!nosess && sess->num_connections)
62 		return;
63 
64 	TAILQ_REMOVE(&list[SESSION_LIST].list, &sess->entry, link);
65 	list[SESSION_LIST].num_entries--;
66 
67 	while ((curr = TAILQ_FIRST(&sess->connections)) != NULL) {
68 		TAILQ_REMOVE(&sess->connections, curr, link);
69 		free(curr);
70 	}
71 	free(sess);
72 }
73 
74 
75 /*
76  * verify_sessions:
77  *    Verify that all sessions in the list still exist.
78  */
79 
80 void
81 verify_sessions(void)
82 {
83 	generic_entry_t *curr, *next;
84 
85 	for (curr = list[SESSION_LIST].list.tqh_first; curr != NULL; curr = next) {
86 		next = curr->link.tqe_next;
87 		verify_session((session_t *) curr);
88 	}
89 }
90 
91 #endif
92 
93 /* -------------------------------------------------------------------------- */
94 
95 /*
96  * find_id:
97  *    Find a list element by ID.
98  *
99  *    Parameter:  the list head and the ID to search for
100  *
101  *    Returns:    The pointer to the element (or NULL if not found)
102  */
103 
104 generic_entry_t *
105 find_id(generic_list_t * head, uint32_t id)
106 {
107 	generic_entry_t *curr;
108 
109 	if (!id)
110 		return NULL;
111 
112 	TAILQ_FOREACH(curr, head, link)
113 		if (curr->sid.id == id)
114 			break;
115 
116 	return curr;
117 }
118 
119 /*
120  * find_name:
121  *    Find a list entry by name.
122  *
123  *    Parameter:  the list head and the symbolic name to search for
124  *
125  *    Returns:    The pointer to the entry (or NULL if not found)
126  */
127 
128 generic_entry_t *
129 find_name(generic_list_t * head, uint8_t * name)
130 {
131 	generic_entry_t *curr;
132 
133 	if (!*name)
134 		return NULL;
135 
136 	TAILQ_FOREACH(curr, head, link)
137 		if (strcmp((char *)curr->sid.name, (char *)name) == 0)
138 			break;
139 
140 	return curr;
141 }
142 
143 
144 /*
145  * find_sym_id:
146  *    Find a list entry by name or numeric id.
147  *
148  *    Parameter:  the list head and the symbolic id to search for
149  *
150  *    Returns:    The pointer to the entry (or NULL if not found)
151  */
152 
153 generic_entry_t *
154 find_sym_id(generic_list_t * head, iscsid_sym_id_t * sid)
155 {
156 
157 	if (sid->id != 0)
158 		return find_id(head, sid->id);
159 
160 	return (sid->name[0]) ? find_name(head, sid->name) : NULL;
161 }
162 
163 
164 /*
165  * get_id:
166  *    Get the numeric ID for a symbolic ID
167  *
168  *    Parameter:  the list head and the symbolic id
169  *
170  *    Returns:    The numeric ID (0 if not found)
171  */
172 
173 uint32_t
174 get_id(generic_list_t * head, iscsid_sym_id_t * sid)
175 {
176 	generic_entry_t *ent;
177 
178 	if (sid->id != 0)
179 		return sid->id;
180 
181 	ent = find_name(head, sid->name);
182 	return (ent != NULL) ? ent->sid.id : 0;
183 }
184 
185 
186 /*
187  * find_target_name:
188  *    Find a target by TargetName.
189  *
190  *    Parameter:  the target name
191  *
192  *    Returns:    The pointer to the target (or NULL if not found)
193  */
194 
195 target_t *
196 find_target(iscsid_list_kind_t lst, iscsid_sym_id_t * sid)
197 {
198 	target_t *targ;
199 
200 	if ((targ = (target_t *)(void *)find_sym_id (&list [lst].list, sid)) != NULL)
201 		return targ;
202 	if (lst == TARGET_LIST) {
203 		portal_t *portal;
204 
205 		if ((portal = (void *)find_portal (sid)) != NULL)
206 			return portal->target;
207 	}
208 	return NULL;
209 }
210 
211 
212 /*
213  * find_target_name:
214  *    Find a target by TargetName.
215  *
216  *    Parameter:  the target name
217  *
218  *    Returns:    The pointer to the target (or NULL if not found)
219  */
220 
221 target_t *
222 find_TargetName(iscsid_list_kind_t lst, uint8_t * name)
223 {
224 	generic_entry_t *curr;
225 	target_t *t = NULL;
226 
227 	if (lst == PORTAL_LIST)
228 		lst = TARGET_LIST;
229 
230 	TAILQ_FOREACH(curr, &list[lst].list, link) {
231 		t = (void *)curr;
232 		if (strcmp((char *)t->TargetName, (char *)name) == 0)
233 			break;
234 	}
235 
236 	DEB(10, ("Find_TagetName returns %p\n", curr));
237 
238 	return t;
239 }
240 
241 
242 /*
243  * find_portal_by_addr:
244  *    Find a Portal by Address.
245  *
246  *    Parameter:  the associated target, and the address
247  *
248  *    Returns:    The pointer to the portal (or NULL if not found)
249  */
250 
251 portal_t *
252 find_portal_by_addr(target_t * target, iscsi_portal_address_t * addr)
253 {
254 	generic_entry_t *curr;
255 	portal_t *p = NULL;
256 
257 	TAILQ_FOREACH(curr, &list[PORTAL_LIST].list, link) {
258 		p = (void *)curr;
259 		DEB(10, ("Find_portal_by_addr - addr %s port %d target %x\n",
260 				 p->addr.address,
261 				 p->addr.port,
262 				 (int) p->target));
263 
264 		if (strcmp((char *)p->addr.address, (char *)addr->address) == 0 &&
265 			(!addr->port || p->addr.port == addr->port) &&
266 			p->target == target)
267 			break;
268 	}
269 
270 	DEB(10, ("Find_portal_by_addr returns %p\n", curr));
271 	return p;
272 }
273 
274 
275 /*
276  * find_send_target_by_addr:
277  *    Find a Send Target by Address.
278  *
279  *    Parameter:  the address
280  *
281  *    Returns:    The pointer to the portal (or NULL if not found)
282  */
283 
284 send_target_t *
285 find_send_target_by_addr(iscsi_portal_address_t * addr)
286 {
287 	generic_entry_t *curr;
288 	send_target_t *t = NULL;
289 
290 	TAILQ_FOREACH(curr, &list[SEND_TARGETS_LIST].list, link) {
291 		t = (void *)curr;
292 		if (strcmp((char *)t->addr.address, (char *)addr->address) == 0 &&
293 			(!addr->port || t->addr.port == addr->port))
294 			break;
295 	}
296 
297 	DEB(10, ("Find_send_target_by_addr returns %p\n", curr));
298 	return t;
299 }
300 
301 
302 /*
303  * get_list:
304  *    Handle GET_LIST request: Return the list of IDs contained in the list.
305  *
306  *    Parameter:
307  *          par         The request parameters.
308  *          prsp        Pointer to address of response buffer.
309  *          prsp_temp   Will be set to TRUE if buffer was allocated, FALSE
310  *                      for static buffer.
311  */
312 
313 void
314 get_list(iscsid_get_list_req_t * par, iscsid_response_t ** prsp, int *prsp_temp)
315 {
316 	iscsid_get_list_rsp_t *res;
317 	iscsid_response_t *rsp = *prsp;
318 	int num;
319 	uint32_t *idp;
320 	generic_list_t *plist;
321 	generic_entry_t *curr;
322 
323 	DEB(10, ("get_list, kind %d\n", par->list_kind));
324 
325 	if (par->list_kind == SESSION_LIST)
326 		LOCK_SESSIONS;
327 	else if (par->list_kind >= NUM_DAEMON_LISTS) {
328 		rsp->status = ISCSID_STATUS_INVALID_PARAMETER;
329 		return;
330 	}
331 
332 	plist = &list[par->list_kind].list;
333 	num = list[par->list_kind].num_entries;
334 
335 	if (!num) {
336 		if (par->list_kind == SESSION_LIST)
337 			UNLOCK_SESSIONS;
338 		rsp->status = ISCSID_STATUS_LIST_EMPTY;
339 		return;
340 	}
341 
342 	rsp = make_rsp(sizeof(iscsid_get_list_rsp_t) +
343 					(num - 1) * sizeof(uint32_t), prsp, prsp_temp);
344 	if (rsp == NULL) {
345 		if (par->list_kind == SESSION_LIST)
346 			UNLOCK_SESSIONS;
347 		return;
348 	}
349 	/* copy the ID of all list entries */
350 	res = (iscsid_get_list_rsp_t *)(void *)rsp->parameter;
351 	res->num_entries = num;
352 	idp = res->id;
353 
354 	TAILQ_FOREACH(curr, plist, link)
355 		* idp++ = curr->sid.id;
356 
357 	if (par->list_kind == SESSION_LIST)
358 		UNLOCK_SESSIONS;
359 }
360 
361 
362 /*
363  * search_list:
364  *    Handle SEARCH_LIST request: Search the given list for the string or
365  *    address.
366  *    Note: Not all combinations of list and search type make sense.
367  *
368  *    Parameter:
369  *          par         The request parameters.
370  *          prsp        Pointer to address of response buffer.
371  *          prsp_temp   Will be set to TRUE if buffer was allocated, FALSE
372  *                      for static buffer.
373  */
374 
375 void
376 search_list(iscsid_search_list_req_t * par, iscsid_response_t ** prsp,
377 			int *prsp_temp)
378 {
379 	iscsid_response_t *rsp = *prsp;
380 	generic_entry_t *elem = NULL;
381 
382 	DEB(10, ("search_list, list_kind %d, search_kind %d\n",
383 			 par->list_kind, par->search_kind));
384 
385 	if (par->list_kind == SESSION_LIST)
386 		LOCK_SESSIONS;
387 	else if (par->list_kind >= NUM_DAEMON_LISTS) {
388 		rsp->status = ISCSID_STATUS_INVALID_PARAMETER;
389 		return;
390 	}
391 
392 	if (!list[par->list_kind].num_entries) {
393 		if (par->list_kind == SESSION_LIST)
394 			UNLOCK_SESSIONS;
395 		rsp->status = ISCSID_STATUS_NOT_FOUND;
396 		return;
397 	}
398 
399 	switch (par->search_kind) {
400 	case FIND_ID:
401 		elem = find_id(&list[par->list_kind].list, par->intval);
402 		break;
403 
404 	case FIND_NAME:
405 		elem = find_name(&list[par->list_kind].list, par->strval);
406 		break;
407 
408 	case FIND_TARGET_NAME:
409 		switch (par->list_kind) {
410 		case TARGET_LIST:
411 		case PORTAL_LIST:
412 		case SEND_TARGETS_LIST:
413 			elem = (void *)find_TargetName(par->list_kind,
414 														par->strval);
415 			break;
416 
417 		case SESSION_LIST:
418 			TAILQ_FOREACH(elem, &list[SESSION_LIST].list, link)
419 				if (strcmp((char *)((session_t *)(void *)elem)->target.TargetName,
420 							(char *)par->strval) == 0)
421 					break;
422 			break;
423 
424 		default:
425 			rsp->status = ISCSID_STATUS_INVALID_PARAMETER;
426 			break;
427 		}
428 		break;
429 
430 	case FIND_ADDRESS:
431 		switch (par->list_kind) {
432 		case PORTAL_LIST:
433 			TAILQ_FOREACH(elem, &list[PORTAL_LIST].list, link) {
434 				portal_t *p = (void *)elem;
435 				if (strcmp((char *)p->addr.address, (char *)par->strval) == 0 &&
436 					(!par->intval ||
437 					 p->addr.port == par->intval))
438 					break;
439 			}
440 			break;
441 
442 		case SEND_TARGETS_LIST:
443 			TAILQ_FOREACH(elem, &list[SEND_TARGETS_LIST].list, link) {
444 				send_target_t *t = (void *)elem;
445 				if (strcmp((char *)t->addr.address,
446 							(char *)par->strval) == 0 &&
447 					(!par->intval ||
448 					 t->addr.port == par->intval))
449 					break;
450 			}
451 			break;
452 
453 		case ISNS_LIST:
454 			TAILQ_FOREACH(elem, &list[ISNS_LIST].list, link) {
455 				isns_t *i = (void *)elem;
456 				if (strcmp((char *)i->address, (char *)par->strval) == 0 &&
457 					(!par->intval || i->port == par->intval))
458 					break;
459 			}
460 			break;
461 
462 		default:
463 			rsp->status = ISCSID_STATUS_INVALID_PARAMETER;
464 			break;
465 		}
466 		break;
467 
468 	default:
469 		rsp->status = ISCSID_STATUS_INVALID_PARAMETER;
470 		return;
471 	}
472 
473 	if (elem == NULL) {
474 		if (par->list_kind == SESSION_LIST)
475 			UNLOCK_SESSIONS;
476 		rsp->status = ISCSID_STATUS_NOT_FOUND;
477 		return;
478 	}
479 
480 	rsp = make_rsp(sizeof(iscsid_sym_id_t), prsp, prsp_temp);
481 	if (rsp == NULL) {
482 		if (par->list_kind == SESSION_LIST)
483 			UNLOCK_SESSIONS;
484 		return;
485 	}
486 
487 	(void) memcpy(rsp->parameter, &elem->sid, sizeof(elem->sid));
488 	if (par->list_kind == SESSION_LIST)
489 		UNLOCK_SESSIONS;
490 }
491 
492 
493 /*
494  * get_session_list:
495  *    Handle GET_SESSION_LIST request: Return a list of sessions complete
496  *    with basic session info.
497  *
498  *    Parameter:
499  *          prsp        Pointer to address of response buffer.
500  *          prsp_temp   Will be set to TRUE if buffer was allocated, FALSE
501  *                      for static buffer.
502  */
503 
504 void
505 get_session_list(iscsid_response_t ** prsp, int *prsp_temp)
506 {
507 	iscsid_get_session_list_rsp_t *res;
508 	iscsid_response_t *rsp = *prsp;
509 	iscsid_session_list_entry_t *ent;
510 	generic_list_t *plist;
511 	generic_entry_t *curr;
512 	session_t *sess;
513 	connection_t *conn;
514 	int num;
515 
516 	DEB(10, ("get_session_list\n"));
517 
518 	LOCK_SESSIONS;
519 	plist = &list[SESSION_LIST].list;
520 	num = list[SESSION_LIST].num_entries;
521 
522 	if (!num) {
523 		UNLOCK_SESSIONS;
524 		rsp->status = ISCSID_STATUS_LIST_EMPTY;
525 		return;
526 	}
527 
528 	rsp = make_rsp(sizeof(iscsid_get_session_list_rsp_t) +
529 				   (num - 1) * sizeof(iscsid_session_list_entry_t),
530 					prsp, prsp_temp);
531 	if (rsp == NULL) {
532 		UNLOCK_SESSIONS;
533 		return;
534 	}
535 	/* copy the ID of all list entries */
536 	res = (iscsid_get_session_list_rsp_t *)(void *)rsp->parameter;
537 	res->num_entries = num;
538 	ent = res->session;
539 
540 	TAILQ_FOREACH(curr, plist, link) {
541 		sess = (session_t *)(void *)curr;
542 		conn = (connection_t *)(void *)TAILQ_FIRST(&sess->connections);
543 
544 		ent->session_id = sess->entry.sid;
545 		ent->first_connection_id = conn->entry.sid.id;
546 		ent->num_connections = sess->num_connections;
547 		ent->portal_id = conn->portal.sid.id;
548 		ent->initiator_id = conn->initiator_id;
549 		ent++;
550 	}
551 	UNLOCK_SESSIONS;
552 }
553 
554 /*
555  * get_connection_list:
556  *    Handle GET_CONNECTION_LIST request: Return a list of connections
557  *    for a session.
558  *
559  *    Parameter:
560  *          prsp        Pointer to address of response buffer.
561  *          prsp_temp   Will be set to TRUE if buffer was allocated, FALSE
562  *                      for static buffer.
563  */
564 
565 void
566 get_connection_list(iscsid_sym_id_t *req, iscsid_response_t **prsp,
567 					int *prsp_temp)
568 {
569 	iscsid_get_connection_list_rsp_t *res;
570 	iscsid_response_t *rsp = *prsp;
571 	iscsid_connection_list_entry_t *ent;
572 	generic_entry_t *curr;
573 	session_t *sess;
574 	connection_t *conn;
575 	int num;
576 
577 	DEB(10, ("get_connection_list\n"));
578 
579 	LOCK_SESSIONS;
580 	if ((sess = find_session(req)) == NULL) {
581 		UNLOCK_SESSIONS;
582 		rsp->status = ISCSID_STATUS_INVALID_SESSION_ID;
583 		return;
584 	}
585 
586 	num = sess->num_connections;
587 	rsp = make_rsp(sizeof(iscsid_get_connection_list_rsp_t) +
588 				   (num - 1) * sizeof(iscsid_connection_list_entry_t),
589 					prsp, prsp_temp);
590 	if (rsp == NULL) {
591 		UNLOCK_SESSIONS;
592 		return;
593 	}
594 	/* copy the ID of all list entries */
595 	res = (iscsid_get_connection_list_rsp_t *)(void *)rsp->parameter;
596 	res->num_connections = num;
597 	ent = res->connection;
598 
599 	TAILQ_FOREACH(curr, &sess->connections, link) {
600 		conn = (connection_t *)(void *)curr;
601 		ent->connection_id = conn->entry.sid;
602 		ent->target_portal_id = conn->portal.sid;
603 		ent->target_portal = conn->portal.addr;
604 		ent++;
605 	}
606 	UNLOCK_SESSIONS;
607 }
608 
609 
610 /*
611  * get_connection_info:
612  *    Handle GET_CONNECTION_INFO request: Return information about a connection
613  *
614  *    Parameter:
615  *          par         The request parameters.
616  *          prsp        Pointer to address of response buffer.
617  *          prsp_temp   Will be set to TRUE if buffer was allocated, FALSE
618  *                      for static buffer.
619  */
620 
621 void
622 get_connection_info(iscsid_get_connection_info_req_t * req,
623 					iscsid_response_t ** prsp, int *prsp_temp)
624 {
625 	iscsid_get_connection_info_rsp_t *res;
626 	iscsid_response_t *rsp = *prsp;
627 	session_t *sess;
628 	connection_t *conn;
629 	initiator_t *init = NULL;
630 
631 	DEB(10, ("get_connection_info, session %d, connection %d\n",
632 			 req->session_id.id, req->connection_id.id));
633 
634 	LOCK_SESSIONS;
635 	if ((sess = find_session(&req->session_id)) == NULL) {
636 		UNLOCK_SESSIONS;
637 		rsp->status = ISCSID_STATUS_INVALID_SESSION_ID;
638 		return;
639 	}
640 	if (!req->connection_id.id && !req->connection_id.name[0]) {
641 		conn = (connection_t *)(void *)TAILQ_FIRST(&sess->connections);
642 	} else if ((conn = find_connection(sess, &req->connection_id)) == NULL) {
643 		UNLOCK_SESSIONS;
644 		rsp->status = ISCSID_STATUS_INVALID_CONNECTION_ID;
645 		return;
646 	}
647 
648 	rsp = make_rsp(sizeof(iscsid_get_connection_info_rsp_t), prsp, prsp_temp);
649 	if (rsp == NULL) {
650 		UNLOCK_SESSIONS;
651 		return;
652 	}
653 
654 	if (conn->initiator_id)
655 		init = find_initiator_id(conn->initiator_id);
656 
657 	res = (iscsid_get_connection_info_rsp_t *)(void *)rsp->parameter;
658 
659 	res->session_id = sess->entry.sid;
660 	res->connection_id = conn->entry.sid;
661 	res->target_portal_id = conn->portal.sid;
662 	res->target_portal = conn->portal.addr;
663 	strlcpy((char *)res->TargetName, (char *)conn->target.TargetName,
664 		sizeof(res->TargetName));
665 	strlcpy((char *)res->TargetAlias, (char *)conn->target.TargetAlias,
666 		sizeof(res->TargetAlias));
667 	if (init != NULL) {
668 		res->initiator_id = init->entry.sid;
669 		strlcpy((char *)res->initiator_address, (char *)init->address,
670 			sizeof(res->initiator_address));
671 	}
672 	UNLOCK_SESSIONS;
673 }
674 
675 /* ------------------------------------------------------------------------- */
676 
677 /*
678  * find_initator_by_addr:
679  *    Find an Initiator Portal by Address.
680  *
681  *    Parameter:  the address
682  *
683  *    Returns:    The pointer to the portal (or NULL if not found)
684  */
685 
686 static initiator_t *
687 find_initiator_by_addr(uint8_t * addr)
688 {
689 	generic_entry_t *curr;
690 	initiator_t *i = NULL;
691 
692 	TAILQ_FOREACH(curr, &list[INITIATOR_LIST].list, link) {
693 		i = (void *)curr;
694 		if (strcmp((char *)i->address, (char *)addr) == 0)
695 			break;
696 	}
697 
698 	DEB(9, ("Find_initiator_by_addr returns %p\n", curr));
699 	return i;
700 }
701 
702 
703 /*
704  * add_initiator_portal:
705  *    Add an initiator portal.
706  *
707  *    Parameter:
708  *          par         The request parameters.
709  *          prsp        Pointer to address of response buffer.
710  *          prsp_temp   Will be set to TRUE if buffer was allocated, FALSE
711  *                      for static buffer.
712  */
713 
714 void
715 add_initiator_portal(iscsid_add_initiator_req_t *par, iscsid_response_t **prsp,
716 					 int *prsp_temp)
717 {
718 	iscsid_add_initiator_rsp_t *res;
719 	iscsid_response_t *rsp = *prsp;
720 	initiator_t *init;
721 
722 	DEB(9, ("AddInitiatorPortal '%s' (name '%s')\n", par->address, par->name));
723 
724 	if (find_initiator_by_addr(par->address) != NULL) {
725 		rsp->status = ISCSID_STATUS_DUPLICATE_ENTRY;
726 		return;
727 	}
728 
729 	if (find_initiator_name(par->name) != NULL) {
730 		rsp->status = ISCSID_STATUS_DUPLICATE_NAME;
731 		return;
732 	}
733 
734 	if ((init = calloc(1, sizeof(*init))) == NULL) {
735 		rsp->status = ISCSID_STATUS_NO_RESOURCES;
736 		return;
737 	}
738 
739 	DEB(9, ("AddInitiatorPortal initiator_id = %d\n", initiator_id));
740 
741 	for (initiator_id++;
742 		 !initiator_id || find_initiator_id(initiator_id) != NULL;)
743 		initiator_id++;
744 
745 	init->entry.sid.id = initiator_id;
746 	strlcpy((char *)init->entry.sid.name, (char *)par->name, sizeof(init->entry.sid.name));
747 	strlcpy((char *)init->address, (char *)par->address, sizeof(init->address));
748 
749 	rsp = make_rsp(sizeof(iscsid_add_initiator_rsp_t), prsp, prsp_temp);
750 	if (rsp == NULL)
751 		return;
752 
753 	LOCK_SESSIONS;
754 	TAILQ_INSERT_TAIL(&list[INITIATOR_LIST].list, &init->entry, link);
755 	list[INITIATOR_LIST].num_entries++;
756 	UNLOCK_SESSIONS;
757 
758 	res = (iscsid_add_initiator_rsp_t *)(void *)rsp->parameter;
759 	res->portal_id = init->entry.sid.id;
760 }
761 
762 
763 /*
764  * remove_initiator_portal:
765  *    Handle REMOVE_INITIATOR request: Removes an initiator entry.
766  *
767  *    Parameter:
768  *          par         The request parameter containing the ID.
769  *
770  *    Returns:     status
771  */
772 
773 uint32_t
774 remove_initiator_portal(iscsid_sym_id_t * par)
775 {
776 	initiator_t *init;
777 
778 	if ((init = find_initiator(par)) == NULL)
779 		return ISCSID_STATUS_INVALID_INITIATOR_ID;
780 
781 	LOCK_SESSIONS;
782 	list[INITIATOR_LIST].num_entries--;
783 
784 	TAILQ_REMOVE(&list[INITIATOR_LIST].list, &init->entry, link);
785 	UNLOCK_SESSIONS;
786 
787 	free(init);
788 
789 	return ISCSID_STATUS_SUCCESS;
790 }
791 
792 
793 
794 /*
795  * get_initiator_portal:
796  *    Handle GET_INITIATOR_PORTAL request: Return information about the given
797  *    initiator portal.
798  *
799  *    Parameter:
800  *          par         The request parameters.
801  *          prsp        Pointer to address of response buffer.
802  *          prsp_temp   Will be set to TRUE if buffer was allocated, FALSE
803  *                      for static buffer.
804  */
805 
806 void
807 get_initiator_portal(iscsid_sym_id_t *par, iscsid_response_t **prsp,
808 					 int *prsp_temp)
809 {
810 	iscsid_get_initiator_rsp_t *res;
811 	iscsid_response_t *rsp = *prsp;
812 	initiator_t *init;
813 
814 	DEB(10, ("get_initiator_portal, id %d (%s)\n", par->id, par->name));
815 
816 	if ((init = find_initiator(par)) == NULL) {
817 		rsp->status = ISCSID_STATUS_INVALID_INITIATOR_ID;
818 		return;
819 	}
820 
821 	rsp = make_rsp(sizeof(iscsid_get_initiator_rsp_t), prsp, prsp_temp);
822 	if (rsp == NULL)
823 		return;
824 
825 	res = (iscsid_get_initiator_rsp_t *)(void *)rsp->parameter;
826 	res->portal_id = init->entry.sid;
827 	strlcpy((char *)res->address, (char *)init->address, sizeof(res->address));
828 }
829 
830 
831 /*
832  * select_initiator:
833  *    Select the initiator portal to use.
834  *    Selects the portal with the least number of active connections.
835  *
836  *    Returns:
837  *       Pointer to the portal, NULL if no portals are defined.
838  *
839  *    NOTE: Called with session list locked, so don't lock again.
840  */
841 
842 initiator_t *
843 select_initiator(void)
844 {
845 	generic_entry_t *curr;
846 	initiator_t *imin = NULL;
847 	uint32_t ccnt = 64 * 1024;	/* probably not more than 64k connections... */
848 
849 	if (!list[INITIATOR_LIST].num_entries)
850 		return NULL;
851 
852 	TAILQ_FOREACH(curr, &list[INITIATOR_LIST].list, link) {
853 		initiator_t *i = (void *)curr;
854 		if ((i->active_connections < ccnt)) {
855 			ccnt = i->active_connections;
856 			imin = i;
857 		}
858 	}
859 	return imin;
860 }
861 
862 /* ------------------------------------------------------------------------- */
863 
864 /*
865  * event_kill_session:
866  *    Handle SESSION_TERMINATED event: Remove session and all associated
867  *    connections.
868  *
869  *    Parameter:
870  *          sid         Session ID
871  */
872 
873 void
874 event_kill_session(uint32_t sid)
875 {
876 	session_t *sess;
877 	connection_t *conn;
878 	portal_t *portal;
879 	initiator_t *init;
880 
881 	LOCK_SESSIONS;
882 
883 	sess = find_session_id(sid);
884 
885 	if (sess == NULL) {
886 		UNLOCK_SESSIONS;
887 		return;
888 	}
889 
890 	TAILQ_REMOVE(&list[SESSION_LIST].list, &sess->entry, link);
891 	list[SESSION_LIST].num_entries--;
892 
893 	UNLOCK_SESSIONS;
894 
895 	while ((conn = (connection_t *)(void *)TAILQ_FIRST(&sess->connections)) != NULL) {
896 		TAILQ_REMOVE(&sess->connections, &conn->entry, link);
897 
898 		portal = find_portal_id(conn->portal.sid.id);
899 		if (portal != NULL)
900 			portal->active_connections--;
901 
902 		init = find_initiator_id(conn->initiator_id);
903 		if (init != NULL)
904 			init->active_connections--;
905 
906 		free(conn);
907 	}
908 	free(sess);
909 }
910 
911 
912 /*
913  * event_kill_connection:
914  *    Handle CONNECTION_TERMINATED event: Remove connection from session.
915  *
916  *    Parameter:
917  *          sid         Session ID
918  *          cid         Connection ID
919  */
920 
921 void
922 event_kill_connection(uint32_t sid, uint32_t cid)
923 {
924 	session_t *sess;
925 	connection_t *conn;
926 	portal_t *portal;
927 	initiator_t *init;
928 
929 	LOCK_SESSIONS;
930 
931 	sess = find_session_id(sid);
932 	if (sess == NULL) {
933 		UNLOCK_SESSIONS;
934 		return;
935 	}
936 
937 	conn = find_connection_id(sess, cid);
938 	if (conn == NULL) {
939 		UNLOCK_SESSIONS;
940 		return;
941 	}
942 
943 	TAILQ_REMOVE(&sess->connections, &conn->entry, link);
944 	sess->num_connections--;
945 
946 	init = find_initiator_id(conn->initiator_id);
947 	if (init != NULL)
948 		init->active_connections--;
949 
950 	UNLOCK_SESSIONS;
951 
952 	portal = find_portal_id(conn->portal.sid.id);
953 	if (portal != NULL)
954 		portal->active_connections--;
955 
956 	free(conn);
957 }
958