6fea88c26e
- debug log is now using syslog - seperate options for log level and foreground mode - writes a pidfile so that /etc/rc.d/iscsid works Now links with libutil for pidfile(), the functions login() and logout() needed to be renamed to avoid a conflict. - drops the nothreads option - handles signals to shut down gracefully - the driver may also shut down the daemon when it terminates Currently this cannot work as the driver can only terminate when the daemon has closed the driver file handle.
976 lines
23 KiB
C
976 lines
23 KiB
C
/* $NetBSD: iscsid_lists.c,v 1.9 2016/05/29 13:35:45 mlelstv Exp $ */
|
|
|
|
/*-
|
|
* Copyright (c) 2005,2006,2011 The NetBSD Foundation, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This code is derived from software contributed to The NetBSD Foundation
|
|
* by Wasabi Systems, Inc.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
|
|
#include "iscsid_globals.h"
|
|
|
|
/* counter for initiator ID */
|
|
static uint32_t initiator_id = 0;
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
#if 0
|
|
|
|
/*
|
|
* verify_session:
|
|
* Verify that a specific session still exists, delete it if not.
|
|
*
|
|
* Parameter: The session pointer.
|
|
*/
|
|
|
|
static void
|
|
verify_session(session_t * sess)
|
|
{
|
|
generic_entry_t *curr, *next;
|
|
int nosess = 0;
|
|
|
|
for (curr = sess->connections.tqh_first; curr != NULL && !nosess; curr = next) {
|
|
next = curr->link.tqe_next;
|
|
nosess = verify_connection((connection_t *) curr) == ISCSI_STATUS_INVALID_SESSION_ID;
|
|
}
|
|
|
|
if (!nosess && sess->num_connections)
|
|
return;
|
|
|
|
TAILQ_REMOVE(&list[SESSION_LIST].list, &sess->entry, link);
|
|
list[SESSION_LIST].num_entries--;
|
|
|
|
while ((curr = TAILQ_FIRST(&sess->connections)) != NULL) {
|
|
TAILQ_REMOVE(&sess->connections, curr, link);
|
|
free(curr);
|
|
}
|
|
free(sess);
|
|
}
|
|
|
|
|
|
/*
|
|
* verify_sessions:
|
|
* Verify that all sessions in the list still exist.
|
|
*/
|
|
|
|
void
|
|
verify_sessions(void)
|
|
{
|
|
generic_entry_t *curr, *next;
|
|
|
|
for (curr = list[SESSION_LIST].list.tqh_first; curr != NULL; curr = next) {
|
|
next = curr->link.tqe_next;
|
|
verify_session((session_t *) curr);
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
/*
|
|
* find_id:
|
|
* Find a list element by ID.
|
|
*
|
|
* Parameter: the list head and the ID to search for
|
|
*
|
|
* Returns: The pointer to the element (or NULL if not found)
|
|
*/
|
|
|
|
generic_entry_t *
|
|
find_id(generic_list_t * head, uint32_t id)
|
|
{
|
|
generic_entry_t *curr;
|
|
|
|
if (!id)
|
|
return NULL;
|
|
|
|
TAILQ_FOREACH(curr, head, link)
|
|
if (curr->sid.id == id)
|
|
break;
|
|
|
|
return curr;
|
|
}
|
|
|
|
/*
|
|
* find_name:
|
|
* Find a list entry by name.
|
|
*
|
|
* Parameter: the list head and the symbolic name to search for
|
|
*
|
|
* Returns: The pointer to the entry (or NULL if not found)
|
|
*/
|
|
|
|
generic_entry_t *
|
|
find_name(generic_list_t * head, uint8_t * name)
|
|
{
|
|
generic_entry_t *curr;
|
|
|
|
if (!*name)
|
|
return NULL;
|
|
|
|
TAILQ_FOREACH(curr, head, link)
|
|
if (strcmp((char *)curr->sid.name, (char *)name) == 0)
|
|
break;
|
|
|
|
return curr;
|
|
}
|
|
|
|
|
|
/*
|
|
* find_sym_id:
|
|
* Find a list entry by name or numeric id.
|
|
*
|
|
* Parameter: the list head and the symbolic id to search for
|
|
*
|
|
* Returns: The pointer to the entry (or NULL if not found)
|
|
*/
|
|
|
|
generic_entry_t *
|
|
find_sym_id(generic_list_t * head, iscsid_sym_id_t * sid)
|
|
{
|
|
|
|
if (sid->id != 0)
|
|
return find_id(head, sid->id);
|
|
|
|
return (sid->name[0]) ? find_name(head, sid->name) : NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
* get_id:
|
|
* Get the numeric ID for a symbolic ID
|
|
*
|
|
* Parameter: the list head and the symbolic id
|
|
*
|
|
* Returns: The numeric ID (0 if not found)
|
|
*/
|
|
|
|
uint32_t
|
|
get_id(generic_list_t * head, iscsid_sym_id_t * sid)
|
|
{
|
|
generic_entry_t *ent;
|
|
|
|
if (sid->id != 0)
|
|
return sid->id;
|
|
|
|
ent = find_name(head, sid->name);
|
|
return (ent != NULL) ? ent->sid.id : 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* find_target_name:
|
|
* Find a target by TargetName.
|
|
*
|
|
* Parameter: the target name
|
|
*
|
|
* Returns: The pointer to the target (or NULL if not found)
|
|
*/
|
|
|
|
target_t *
|
|
find_target(iscsid_list_kind_t lst, iscsid_sym_id_t * sid)
|
|
{
|
|
target_t *targ;
|
|
|
|
if ((targ = (target_t *)(void *)find_sym_id (&list [lst].list, sid)) != NULL)
|
|
return targ;
|
|
if (lst == TARGET_LIST) {
|
|
portal_t *portal;
|
|
|
|
if ((portal = (void *)find_portal (sid)) != NULL)
|
|
return portal->target;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
* find_target_name:
|
|
* Find a target by TargetName.
|
|
*
|
|
* Parameter: the target name
|
|
*
|
|
* Returns: The pointer to the target (or NULL if not found)
|
|
*/
|
|
|
|
target_t *
|
|
find_TargetName(iscsid_list_kind_t lst, uint8_t * name)
|
|
{
|
|
generic_entry_t *curr;
|
|
target_t *t = NULL;
|
|
|
|
if (lst == PORTAL_LIST)
|
|
lst = TARGET_LIST;
|
|
|
|
TAILQ_FOREACH(curr, &list[lst].list, link) {
|
|
t = (void *)curr;
|
|
if (strcmp((char *)t->TargetName, (char *)name) == 0)
|
|
break;
|
|
}
|
|
|
|
/* return curr instead of t because curr==NULL if name not found */
|
|
DEB(10, ("Find_TargetName returns %p", curr));
|
|
return (target_t *)curr;
|
|
}
|
|
|
|
|
|
/*
|
|
* find_portal_by_addr:
|
|
* Find a Portal by Address.
|
|
*
|
|
* Parameter: the associated target, and the address
|
|
*
|
|
* Returns: The pointer to the portal (or NULL if not found)
|
|
*/
|
|
|
|
portal_t *
|
|
find_portal_by_addr(target_t * target, iscsi_portal_address_t * addr)
|
|
{
|
|
generic_entry_t *curr;
|
|
portal_t *p = NULL;
|
|
|
|
TAILQ_FOREACH(curr, &list[PORTAL_LIST].list, link) {
|
|
p = (void *)curr;
|
|
DEB(10, ("Find_portal_by_addr - addr %s port %d target %p",
|
|
p->addr.address,
|
|
p->addr.port,
|
|
p->target));
|
|
|
|
if (strcmp((char *)p->addr.address, (char *)addr->address) == 0 &&
|
|
(!addr->port || p->addr.port == addr->port) &&
|
|
p->target == target)
|
|
break;
|
|
}
|
|
|
|
/* return curr instead of p because curr==NULL if not found */
|
|
DEB(10, ("Find_portal_by_addr returns %p", curr));
|
|
return (portal_t *)curr;
|
|
}
|
|
|
|
|
|
/*
|
|
* find_send_target_by_addr:
|
|
* Find a Send Target by Address.
|
|
*
|
|
* Parameter: the address
|
|
*
|
|
* Returns: The pointer to the portal (or NULL if not found)
|
|
*/
|
|
|
|
send_target_t *
|
|
find_send_target_by_addr(iscsi_portal_address_t * addr)
|
|
{
|
|
generic_entry_t *curr;
|
|
send_target_t *t = NULL;
|
|
|
|
TAILQ_FOREACH(curr, &list[SEND_TARGETS_LIST].list, link) {
|
|
t = (void *)curr;
|
|
if (strcmp((char *)t->addr.address, (char *)addr->address) == 0 &&
|
|
(!addr->port || t->addr.port == addr->port))
|
|
break;
|
|
}
|
|
|
|
/* return curr instead of p because curr==NULL if not found */
|
|
DEB(10, ("Find_send_target_by_addr returns %p", curr));
|
|
return (send_target_t *)curr;
|
|
}
|
|
|
|
|
|
/*
|
|
* get_list:
|
|
* Handle GET_LIST request: Return the list of IDs contained in the list.
|
|
*
|
|
* Parameter:
|
|
* par The request parameters.
|
|
* prsp Pointer to address of response buffer.
|
|
* prsp_temp Will be set to TRUE if buffer was allocated, FALSE
|
|
* for static buffer.
|
|
*/
|
|
|
|
void
|
|
get_list(iscsid_get_list_req_t * par, iscsid_response_t ** prsp, int *prsp_temp)
|
|
{
|
|
iscsid_get_list_rsp_t *res;
|
|
iscsid_response_t *rsp = *prsp;
|
|
int num;
|
|
uint32_t *idp;
|
|
generic_list_t *plist;
|
|
generic_entry_t *curr;
|
|
|
|
DEB(10, ("get_list, kind %d", par->list_kind));
|
|
|
|
if (par->list_kind == SESSION_LIST)
|
|
LOCK_SESSIONS;
|
|
else if (par->list_kind >= NUM_DAEMON_LISTS) {
|
|
rsp->status = ISCSID_STATUS_INVALID_PARAMETER;
|
|
return;
|
|
}
|
|
|
|
plist = &list[par->list_kind].list;
|
|
num = list[par->list_kind].num_entries;
|
|
|
|
if (!num) {
|
|
if (par->list_kind == SESSION_LIST)
|
|
UNLOCK_SESSIONS;
|
|
rsp->status = ISCSID_STATUS_LIST_EMPTY;
|
|
return;
|
|
}
|
|
|
|
rsp = make_rsp(sizeof(iscsid_get_list_rsp_t) +
|
|
(num - 1) * sizeof(uint32_t), prsp, prsp_temp);
|
|
if (rsp == NULL) {
|
|
if (par->list_kind == SESSION_LIST)
|
|
UNLOCK_SESSIONS;
|
|
return;
|
|
}
|
|
/* copy the ID of all list entries */
|
|
res = (iscsid_get_list_rsp_t *)(void *)rsp->parameter;
|
|
res->num_entries = num;
|
|
idp = res->id;
|
|
|
|
TAILQ_FOREACH(curr, plist, link)
|
|
* idp++ = curr->sid.id;
|
|
|
|
if (par->list_kind == SESSION_LIST)
|
|
UNLOCK_SESSIONS;
|
|
}
|
|
|
|
|
|
/*
|
|
* search_list:
|
|
* Handle SEARCH_LIST request: Search the given list for the string or
|
|
* address.
|
|
* Note: Not all combinations of list and search type make sense.
|
|
*
|
|
* Parameter:
|
|
* par The request parameters.
|
|
* prsp Pointer to address of response buffer.
|
|
* prsp_temp Will be set to TRUE if buffer was allocated, FALSE
|
|
* for static buffer.
|
|
*/
|
|
|
|
void
|
|
search_list(iscsid_search_list_req_t * par, iscsid_response_t ** prsp,
|
|
int *prsp_temp)
|
|
{
|
|
iscsid_response_t *rsp = *prsp;
|
|
generic_entry_t *elem = NULL;
|
|
|
|
DEB(10, ("search_list, list_kind %d, search_kind %d",
|
|
par->list_kind, par->search_kind));
|
|
|
|
if (par->list_kind == SESSION_LIST)
|
|
LOCK_SESSIONS;
|
|
else if (par->list_kind >= NUM_DAEMON_LISTS) {
|
|
rsp->status = ISCSID_STATUS_INVALID_PARAMETER;
|
|
return;
|
|
}
|
|
|
|
if (!list[par->list_kind].num_entries) {
|
|
if (par->list_kind == SESSION_LIST)
|
|
UNLOCK_SESSIONS;
|
|
rsp->status = ISCSID_STATUS_NOT_FOUND;
|
|
return;
|
|
}
|
|
|
|
switch (par->search_kind) {
|
|
case FIND_ID:
|
|
elem = find_id(&list[par->list_kind].list, par->intval);
|
|
break;
|
|
|
|
case FIND_NAME:
|
|
elem = find_name(&list[par->list_kind].list, par->strval);
|
|
break;
|
|
|
|
case FIND_TARGET_NAME:
|
|
switch (par->list_kind) {
|
|
case TARGET_LIST:
|
|
case PORTAL_LIST:
|
|
case SEND_TARGETS_LIST:
|
|
elem = (void *)find_TargetName(par->list_kind,
|
|
par->strval);
|
|
break;
|
|
|
|
case SESSION_LIST:
|
|
TAILQ_FOREACH(elem, &list[SESSION_LIST].list, link)
|
|
if (strcmp((char *)((session_t *)(void *)elem)->target.TargetName,
|
|
(char *)par->strval) == 0)
|
|
break;
|
|
break;
|
|
|
|
default:
|
|
rsp->status = ISCSID_STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case FIND_ADDRESS:
|
|
switch (par->list_kind) {
|
|
case PORTAL_LIST:
|
|
TAILQ_FOREACH(elem, &list[PORTAL_LIST].list, link) {
|
|
portal_t *p = (void *)elem;
|
|
if (strcmp((char *)p->addr.address, (char *)par->strval) == 0 &&
|
|
(!par->intval ||
|
|
p->addr.port == par->intval))
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case SEND_TARGETS_LIST:
|
|
TAILQ_FOREACH(elem, &list[SEND_TARGETS_LIST].list, link) {
|
|
send_target_t *t = (void *)elem;
|
|
if (strcmp((char *)t->addr.address,
|
|
(char *)par->strval) == 0 &&
|
|
(!par->intval ||
|
|
t->addr.port == par->intval))
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case ISNS_LIST:
|
|
TAILQ_FOREACH(elem, &list[ISNS_LIST].list, link) {
|
|
isns_t *i = (void *)elem;
|
|
if (strcmp((char *)i->address, (char *)par->strval) == 0 &&
|
|
(!par->intval || i->port == par->intval))
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
rsp->status = ISCSID_STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
rsp->status = ISCSID_STATUS_INVALID_PARAMETER;
|
|
return;
|
|
}
|
|
|
|
if (elem == NULL) {
|
|
if (par->list_kind == SESSION_LIST)
|
|
UNLOCK_SESSIONS;
|
|
rsp->status = ISCSID_STATUS_NOT_FOUND;
|
|
return;
|
|
}
|
|
|
|
rsp = make_rsp(sizeof(iscsid_sym_id_t), prsp, prsp_temp);
|
|
if (rsp == NULL) {
|
|
if (par->list_kind == SESSION_LIST)
|
|
UNLOCK_SESSIONS;
|
|
return;
|
|
}
|
|
|
|
(void) memcpy(rsp->parameter, &elem->sid, sizeof(elem->sid));
|
|
if (par->list_kind == SESSION_LIST)
|
|
UNLOCK_SESSIONS;
|
|
}
|
|
|
|
|
|
/*
|
|
* get_session_list:
|
|
* Handle GET_SESSION_LIST request: Return a list of sessions complete
|
|
* with basic session info.
|
|
*
|
|
* Parameter:
|
|
* prsp Pointer to address of response buffer.
|
|
* prsp_temp Will be set to TRUE if buffer was allocated, FALSE
|
|
* for static buffer.
|
|
*/
|
|
|
|
void
|
|
get_session_list(iscsid_response_t ** prsp, int *prsp_temp)
|
|
{
|
|
iscsid_get_session_list_rsp_t *res;
|
|
iscsid_response_t *rsp = *prsp;
|
|
iscsid_session_list_entry_t *ent;
|
|
generic_list_t *plist;
|
|
generic_entry_t *curr;
|
|
session_t *sess;
|
|
connection_t *conn;
|
|
int num;
|
|
|
|
DEB(10, ("get_session_list"));
|
|
|
|
LOCK_SESSIONS;
|
|
plist = &list[SESSION_LIST].list;
|
|
num = list[SESSION_LIST].num_entries;
|
|
|
|
if (!num) {
|
|
UNLOCK_SESSIONS;
|
|
rsp->status = ISCSID_STATUS_LIST_EMPTY;
|
|
return;
|
|
}
|
|
|
|
rsp = make_rsp(sizeof(iscsid_get_session_list_rsp_t) +
|
|
(num - 1) * sizeof(iscsid_session_list_entry_t),
|
|
prsp, prsp_temp);
|
|
if (rsp == NULL) {
|
|
UNLOCK_SESSIONS;
|
|
return;
|
|
}
|
|
/* copy the ID of all list entries */
|
|
res = (iscsid_get_session_list_rsp_t *)(void *)rsp->parameter;
|
|
res->num_entries = num;
|
|
ent = res->session;
|
|
|
|
TAILQ_FOREACH(curr, plist, link) {
|
|
sess = (session_t *)(void *)curr;
|
|
conn = (connection_t *)(void *)TAILQ_FIRST(&sess->connections);
|
|
|
|
ent->session_id = sess->entry.sid;
|
|
ent->num_connections = sess->num_connections;
|
|
if (conn) {
|
|
ent->first_connection_id = conn->entry.sid.id;
|
|
ent->portal_id = conn->portal.sid.id;
|
|
ent->initiator_id = conn->initiator_id;
|
|
} else {
|
|
ent->first_connection_id = 0;
|
|
ent->portal_id = 0;
|
|
ent->initiator_id = 0;
|
|
}
|
|
ent++;
|
|
}
|
|
UNLOCK_SESSIONS;
|
|
}
|
|
|
|
/*
|
|
* get_connection_list:
|
|
* Handle GET_CONNECTION_LIST request: Return a list of connections
|
|
* for a session.
|
|
*
|
|
* Parameter:
|
|
* prsp Pointer to address of response buffer.
|
|
* prsp_temp Will be set to TRUE if buffer was allocated, FALSE
|
|
* for static buffer.
|
|
*/
|
|
|
|
void
|
|
get_connection_list(iscsid_sym_id_t *req, iscsid_response_t **prsp,
|
|
int *prsp_temp)
|
|
{
|
|
iscsid_get_connection_list_rsp_t *res;
|
|
iscsid_response_t *rsp = *prsp;
|
|
iscsid_connection_list_entry_t *ent;
|
|
generic_entry_t *curr;
|
|
session_t *sess;
|
|
connection_t *conn;
|
|
int num;
|
|
|
|
DEB(10, ("get_connection_list"));
|
|
|
|
LOCK_SESSIONS;
|
|
if ((sess = find_session(req)) == NULL) {
|
|
UNLOCK_SESSIONS;
|
|
rsp->status = ISCSID_STATUS_INVALID_SESSION_ID;
|
|
return;
|
|
}
|
|
|
|
num = sess->num_connections;
|
|
rsp = make_rsp(sizeof(iscsid_get_connection_list_rsp_t) +
|
|
(num - 1) * sizeof(iscsid_connection_list_entry_t),
|
|
prsp, prsp_temp);
|
|
if (rsp == NULL) {
|
|
UNLOCK_SESSIONS;
|
|
return;
|
|
}
|
|
/* copy the ID of all list entries */
|
|
res = (iscsid_get_connection_list_rsp_t *)(void *)rsp->parameter;
|
|
res->num_connections = num;
|
|
ent = res->connection;
|
|
|
|
TAILQ_FOREACH(curr, &sess->connections, link) {
|
|
conn = (connection_t *)(void *)curr;
|
|
ent->connection_id = conn->entry.sid;
|
|
ent->target_portal_id = conn->portal.sid;
|
|
ent->target_portal = conn->portal.addr;
|
|
ent++;
|
|
}
|
|
UNLOCK_SESSIONS;
|
|
}
|
|
|
|
|
|
/*
|
|
* get_connection_info:
|
|
* Handle GET_CONNECTION_INFO request: Return information about a connection
|
|
*
|
|
* Parameter:
|
|
* par The request parameters.
|
|
* prsp Pointer to address of response buffer.
|
|
* prsp_temp Will be set to TRUE if buffer was allocated, FALSE
|
|
* for static buffer.
|
|
*/
|
|
|
|
void
|
|
get_connection_info(iscsid_get_connection_info_req_t * req,
|
|
iscsid_response_t ** prsp, int *prsp_temp)
|
|
{
|
|
iscsid_get_connection_info_rsp_t *res;
|
|
iscsid_response_t *rsp = *prsp;
|
|
session_t *sess;
|
|
connection_t *conn;
|
|
initiator_t *init = NULL;
|
|
|
|
DEB(10, ("get_connection_info, session %d, connection %d",
|
|
req->session_id.id, req->connection_id.id));
|
|
|
|
LOCK_SESSIONS;
|
|
if ((sess = find_session(&req->session_id)) == NULL) {
|
|
UNLOCK_SESSIONS;
|
|
rsp->status = ISCSID_STATUS_INVALID_SESSION_ID;
|
|
return;
|
|
}
|
|
if (!req->connection_id.id && !req->connection_id.name[0]) {
|
|
conn = (connection_t *)(void *)TAILQ_FIRST(&sess->connections);
|
|
} else if ((conn = find_connection(sess, &req->connection_id)) == NULL) {
|
|
UNLOCK_SESSIONS;
|
|
rsp->status = ISCSID_STATUS_INVALID_CONNECTION_ID;
|
|
return;
|
|
}
|
|
|
|
rsp = make_rsp(sizeof(iscsid_get_connection_info_rsp_t), prsp, prsp_temp);
|
|
if (rsp == NULL) {
|
|
UNLOCK_SESSIONS;
|
|
return;
|
|
}
|
|
|
|
if (conn && conn->initiator_id)
|
|
init = find_initiator_id(conn->initiator_id);
|
|
|
|
res = (iscsid_get_connection_info_rsp_t *)(void *)rsp->parameter;
|
|
|
|
res->session_id = sess->entry.sid;
|
|
if (conn) {
|
|
res->connection_id = conn->entry.sid;
|
|
res->target_portal_id = conn->portal.sid;
|
|
res->target_portal = conn->portal.addr;
|
|
strlcpy((char *)res->TargetName, (char *)conn->target.TargetName,
|
|
sizeof(res->TargetName));
|
|
strlcpy((char *)res->TargetAlias, (char *)conn->target.TargetAlias,
|
|
sizeof(res->TargetAlias));
|
|
} else {
|
|
res->connection_id.id = 0;
|
|
res->connection_id.name[0] = '\0';
|
|
res->target_portal_id.id = 0;
|
|
res->target_portal_id.name[0] = '\0';
|
|
memset(&res->target_portal, 0, sizeof(res->target_portal));
|
|
memset(&res->TargetName, 0, sizeof(res->TargetName));
|
|
memset(&res->TargetAlias, 0, sizeof(res->TargetAlias));
|
|
}
|
|
if (init != NULL) {
|
|
res->initiator_id = init->entry.sid;
|
|
strlcpy((char *)res->initiator_address, (char *)init->address,
|
|
sizeof(res->initiator_address));
|
|
}
|
|
UNLOCK_SESSIONS;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
/*
|
|
* find_initator_by_addr:
|
|
* Find an Initiator Portal by Address.
|
|
*
|
|
* Parameter: the address
|
|
*
|
|
* Returns: The pointer to the portal (or NULL if not found)
|
|
*/
|
|
|
|
static initiator_t *
|
|
find_initiator_by_addr(uint8_t * addr)
|
|
{
|
|
generic_entry_t *curr;
|
|
initiator_t *i = NULL;
|
|
|
|
TAILQ_FOREACH(curr, &list[INITIATOR_LIST].list, link) {
|
|
i = (void *)curr;
|
|
if (strcmp((char *)i->address, (char *)addr) == 0)
|
|
break;
|
|
}
|
|
|
|
/* return curr instead of i because if not found, curr==NULL */
|
|
DEB(9, ("Find_initiator_by_addr returns %p", curr));
|
|
return (initiator_t *)curr;
|
|
}
|
|
|
|
|
|
/*
|
|
* add_initiator_portal:
|
|
* Add an initiator portal.
|
|
*
|
|
* Parameter:
|
|
* par The request parameters.
|
|
* prsp Pointer to address of response buffer.
|
|
* prsp_temp Will be set to TRUE if buffer was allocated, FALSE
|
|
* for static buffer.
|
|
*/
|
|
|
|
void
|
|
add_initiator_portal(iscsid_add_initiator_req_t *par, iscsid_response_t **prsp,
|
|
int *prsp_temp)
|
|
{
|
|
iscsid_add_initiator_rsp_t *res;
|
|
iscsid_response_t *rsp = *prsp;
|
|
initiator_t *init;
|
|
|
|
DEB(9, ("AddInitiatorPortal '%s' (name '%s')", par->address, par->name));
|
|
|
|
if (find_initiator_by_addr(par->address) != NULL) {
|
|
rsp->status = ISCSID_STATUS_DUPLICATE_ENTRY;
|
|
return;
|
|
}
|
|
|
|
if (find_initiator_name(par->name) != NULL) {
|
|
rsp->status = ISCSID_STATUS_DUPLICATE_NAME;
|
|
return;
|
|
}
|
|
|
|
if ((init = calloc(1, sizeof(*init))) == NULL) {
|
|
rsp->status = ISCSID_STATUS_NO_RESOURCES;
|
|
return;
|
|
}
|
|
|
|
DEB(9, ("AddInitiatorPortal initiator_id = %d", initiator_id));
|
|
|
|
for (initiator_id++;
|
|
!initiator_id || find_initiator_id(initiator_id) != NULL;)
|
|
initiator_id++;
|
|
|
|
init->entry.sid.id = initiator_id;
|
|
strlcpy((char *)init->entry.sid.name, (char *)par->name, sizeof(init->entry.sid.name));
|
|
strlcpy((char *)init->address, (char *)par->address, sizeof(init->address));
|
|
|
|
rsp = make_rsp(sizeof(iscsid_add_initiator_rsp_t), prsp, prsp_temp);
|
|
if (rsp == NULL)
|
|
return;
|
|
|
|
LOCK_SESSIONS;
|
|
TAILQ_INSERT_TAIL(&list[INITIATOR_LIST].list, &init->entry, link);
|
|
list[INITIATOR_LIST].num_entries++;
|
|
UNLOCK_SESSIONS;
|
|
|
|
res = (iscsid_add_initiator_rsp_t *)(void *)rsp->parameter;
|
|
res->portal_id = init->entry.sid.id;
|
|
}
|
|
|
|
|
|
/*
|
|
* remove_initiator_portal:
|
|
* Handle REMOVE_INITIATOR request: Removes an initiator entry.
|
|
*
|
|
* Parameter:
|
|
* par The request parameter containing the ID.
|
|
*
|
|
* Returns: status
|
|
*/
|
|
|
|
uint32_t
|
|
remove_initiator_portal(iscsid_sym_id_t * par)
|
|
{
|
|
initiator_t *init;
|
|
|
|
if ((init = find_initiator(par)) == NULL)
|
|
return ISCSID_STATUS_INVALID_INITIATOR_ID;
|
|
|
|
LOCK_SESSIONS;
|
|
list[INITIATOR_LIST].num_entries--;
|
|
|
|
TAILQ_REMOVE(&list[INITIATOR_LIST].list, &init->entry, link);
|
|
UNLOCK_SESSIONS;
|
|
|
|
free(init);
|
|
|
|
return ISCSID_STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* get_initiator_portal:
|
|
* Handle GET_INITIATOR_PORTAL request: Return information about the given
|
|
* initiator portal.
|
|
*
|
|
* Parameter:
|
|
* par The request parameters.
|
|
* prsp Pointer to address of response buffer.
|
|
* prsp_temp Will be set to TRUE if buffer was allocated, FALSE
|
|
* for static buffer.
|
|
*/
|
|
|
|
void
|
|
get_initiator_portal(iscsid_sym_id_t *par, iscsid_response_t **prsp,
|
|
int *prsp_temp)
|
|
{
|
|
iscsid_get_initiator_rsp_t *res;
|
|
iscsid_response_t *rsp = *prsp;
|
|
initiator_t *init;
|
|
|
|
DEB(10, ("get_initiator_portal, id %d (%s)", par->id, par->name));
|
|
|
|
if ((init = find_initiator(par)) == NULL) {
|
|
rsp->status = ISCSID_STATUS_INVALID_INITIATOR_ID;
|
|
return;
|
|
}
|
|
|
|
rsp = make_rsp(sizeof(iscsid_get_initiator_rsp_t), prsp, prsp_temp);
|
|
if (rsp == NULL)
|
|
return;
|
|
|
|
res = (iscsid_get_initiator_rsp_t *)(void *)rsp->parameter;
|
|
res->portal_id = init->entry.sid;
|
|
strlcpy((char *)res->address, (char *)init->address, sizeof(res->address));
|
|
}
|
|
|
|
|
|
/*
|
|
* select_initiator:
|
|
* Select the initiator portal to use.
|
|
* Selects the portal with the least number of active connections.
|
|
*
|
|
* Returns:
|
|
* Pointer to the portal, NULL if no portals are defined.
|
|
*
|
|
* NOTE: Called with session list locked, so don't lock again.
|
|
*/
|
|
|
|
initiator_t *
|
|
select_initiator(void)
|
|
{
|
|
generic_entry_t *curr;
|
|
initiator_t *imin = NULL;
|
|
uint32_t ccnt = 64 * 1024; /* probably not more than 64k connections... */
|
|
|
|
if (!list[INITIATOR_LIST].num_entries)
|
|
return NULL;
|
|
|
|
TAILQ_FOREACH(curr, &list[INITIATOR_LIST].list, link) {
|
|
initiator_t *i = (void *)curr;
|
|
if ((i->active_connections < ccnt)) {
|
|
ccnt = i->active_connections;
|
|
imin = i;
|
|
}
|
|
}
|
|
return imin;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
/*
|
|
* event_kill_session:
|
|
* Handle SESSION_TERMINATED event: Remove session and all associated
|
|
* connections.
|
|
*
|
|
* Parameter:
|
|
* sid Session ID
|
|
*/
|
|
|
|
void
|
|
event_kill_session(uint32_t sid)
|
|
{
|
|
session_t *sess;
|
|
connection_t *conn;
|
|
portal_t *portal;
|
|
initiator_t *init;
|
|
|
|
LOCK_SESSIONS;
|
|
|
|
sess = find_session_id(sid);
|
|
|
|
if (sess == NULL) {
|
|
UNLOCK_SESSIONS;
|
|
return;
|
|
}
|
|
|
|
TAILQ_REMOVE(&list[SESSION_LIST].list, &sess->entry, link);
|
|
list[SESSION_LIST].num_entries--;
|
|
|
|
UNLOCK_SESSIONS;
|
|
|
|
while ((conn = (connection_t *)(void *)TAILQ_FIRST(&sess->connections)) != NULL) {
|
|
TAILQ_REMOVE(&sess->connections, &conn->entry, link);
|
|
|
|
portal = find_portal_id(conn->portal.sid.id);
|
|
if (portal != NULL)
|
|
portal->active_connections--;
|
|
|
|
init = find_initiator_id(conn->initiator_id);
|
|
if (init != NULL)
|
|
init->active_connections--;
|
|
|
|
free(conn);
|
|
}
|
|
free(sess);
|
|
}
|
|
|
|
|
|
/*
|
|
* event_kill_connection:
|
|
* Handle CONNECTION_TERMINATED event: Remove connection from session.
|
|
*
|
|
* Parameter:
|
|
* sid Session ID
|
|
* cid Connection ID
|
|
*/
|
|
|
|
void
|
|
event_kill_connection(uint32_t sid, uint32_t cid)
|
|
{
|
|
session_t *sess;
|
|
connection_t *conn;
|
|
portal_t *portal;
|
|
initiator_t *init;
|
|
|
|
LOCK_SESSIONS;
|
|
|
|
sess = find_session_id(sid);
|
|
if (sess == NULL) {
|
|
UNLOCK_SESSIONS;
|
|
return;
|
|
}
|
|
|
|
conn = find_connection_id(sess, cid);
|
|
if (conn == NULL) {
|
|
UNLOCK_SESSIONS;
|
|
return;
|
|
}
|
|
|
|
TAILQ_REMOVE(&sess->connections, &conn->entry, link);
|
|
sess->num_connections--;
|
|
|
|
init = find_initiator_id(conn->initiator_id);
|
|
if (init != NULL)
|
|
init->active_connections--;
|
|
|
|
UNLOCK_SESSIONS;
|
|
|
|
portal = find_portal_id(conn->portal.sid.id);
|
|
if (portal != NULL)
|
|
portal->active_connections--;
|
|
|
|
free(conn);
|
|
}
|