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