#include "iscsid_globals.h"
#include <ctype.h>
static uint32_t portarget_id = 0;
static uint32_t send_target_id = 0;
static portal_t *
create_portal(target_t *target, iscsi_portal_address_t *addr,
iscsi_portal_types_t dtype, uint32_t did)
{
portal_group_t *curr;
portal_t *portal;
u_short tag = addr->group_tag;
DEB(9, ("Create Portal addr %s port %d group %d",
addr->address, addr->port, addr->group_tag));
if ((portal = find_portal_by_addr(target, addr)) != NULL)
return portal;
portal = calloc(1, sizeof(*portal));
if (!portal) {
DEBOUT(("Out of memory in create_portal!"));
return NULL;
}
portal->addr = *addr;
portal->target = target;
portal->portaltype = dtype;
portal->discoveryid = did;
if (!portal->addr.port) {
portal->addr.port = ISCSI_DEFAULT_PORT;
}
for (portarget_id++; !portarget_id ||
find_portal_id(portarget_id) != NULL ||
find_target_id(TARGET_LIST, portarget_id) != NULL;) {
portarget_id++;
}
portal->entry.sid.id = portarget_id;
TAILQ_FOREACH(curr, &target->group_list, groups)
if (curr->tag == tag)
break;
if (!curr) {
curr = calloc(1, sizeof(*curr));
if (!curr) {
free(portal);
DEBOUT(("Out of memory in create_portal!"));
return NULL;
}
curr->tag = tag;
TAILQ_INIT(&curr->portals);
TAILQ_INSERT_TAIL(&target->group_list, curr, groups);
target->num_groups++;
}
portal->group = curr;
TAILQ_INSERT_TAIL(&curr->portals, portal, group_list);
curr->num_portals++;
target->num_portals++;
TAILQ_INSERT_TAIL(&list[PORTAL_LIST].list, &portal->entry, link);
list[PORTAL_LIST].num_entries++;
DEB(9, ("create_portal returns %p", portal));
return portal;
}
void
delete_portal(portal_t * portal, boolean_t delete_empty)
{
portal_group_t *curr = portal->group;
target_t *target = portal->target;
TAILQ_REMOVE(&curr->portals, portal, group_list);
TAILQ_REMOVE(&list[PORTAL_LIST].list, &portal->entry, link);
curr->num_portals--;
target->num_portals--;
list[PORTAL_LIST].num_entries--;
if (!curr->num_portals) {
TAILQ_REMOVE(&target->group_list, curr, groups);
free(curr);
target->num_groups--;
}
free(portal);
if (delete_empty && !target->num_portals) {
TAILQ_REMOVE(&list[TARGET_LIST].list, &target->entry, link);
list[TARGET_LIST].num_entries--;
free(target);
}
}
static target_t *
create_target(uint8_t * name)
{
target_t *target;
DEB(9, ("Create Target %s", name));
if ((target = calloc(1, sizeof(*target))) == NULL) {
DEBOUT(("Out of memory in create_target!"));
return NULL;
}
TAILQ_INIT(&target->group_list);
for (portarget_id++;
!portarget_id ||
find_portal_id(portarget_id) != NULL ||
find_target_id(TARGET_LIST, portarget_id) != NULL;
portarget_id++) {
}
target->entry.sid.id = portarget_id;
strlcpy((char *)target->TargetName, (char *)name, sizeof(target->TargetName));
return target;
}
static void
delete_target(target_t * target)
{
portal_group_t *cgroup;
portal_t *curr = NULL;
while (target->num_groups) {
cgroup = TAILQ_FIRST(&target->group_list);
while (cgroup && cgroup->num_portals) {
curr = TAILQ_FIRST(&cgroup->portals);
if (curr)
delete_portal(curr, FALSE);
}
}
TAILQ_REMOVE(&list[TARGET_LIST].list, &target->entry, link);
list[TARGET_LIST].num_entries--;
free(target);
}
static target_t *
create_send_target(uint8_t * name, iscsi_portal_address_t * addr)
{
send_target_t *target;
DEB(9, ("Create Send Target %s", name));
if ((target = calloc(1, sizeof(*target))) == NULL)
return NULL;
for (send_target_id++;
!send_target_id
|| find_target_id(SEND_TARGETS_LIST, send_target_id) != NULL;)
send_target_id++;
target->entry.sid.id = send_target_id;
strlcpy((char *)target->TargetName, (char *)name, sizeof(target->TargetName));
target->addr = *addr;
target->num_groups = 1;
target->num_portals = 1;
return (target_t *)(void *)target;
}
static void
delete_send_target(send_target_t * send_target)
{
generic_entry_t *curr;
uint32_t id;
id = send_target->entry.sid.id;
TAILQ_FOREACH(curr, &list[PORTAL_LIST].list, link) {
portal_t *p = (void *)curr;
if (p->portaltype == PORTAL_TYPE_SENDTARGET &&
p->discoveryid == id)
p->discoveryid = 0;
}
TAILQ_REMOVE(&list[SEND_TARGETS_LIST].list, &send_target->entry, link);
list[SEND_TARGETS_LIST].num_entries--;
free(send_target);
}
void
add_target(iscsid_add_target_req_t *par, iscsid_response_t **prsp,
int *prsp_temp)
{
iscsid_add_target_rsp_t *res;
iscsid_response_t *rsp = *prsp;
target_t *target, *tn;
portal_t *portal;
int i, num;
DEB(9, ("Add Target, name %s, num_portals %d",
par->TargetName, par->num_portals));
if (par->list_kind == SEND_TARGETS_LIST && par->num_portals != 1) {
rsp->status = ISCSID_STATUS_PARAMETER_INVALID;
return;
}
if ((par->TargetName[0] &&
(target = find_TargetName(par->list_kind, par->TargetName)) != NULL) ||
(par->list_kind == SEND_TARGETS_LIST &&
(target = (target_t *)(void *)
find_send_target_by_addr(&par->portal[0])) != NULL)) {
num = target->num_portals;
if (par->sym_name[0]) {
tn = find_target_symname(par->list_kind, par->sym_name);
if (tn && tn != target) {
rsp->status = ISCSID_STATUS_DUPLICATE_NAME;
return;
}
strlcpy((char *)target->entry.sid.name, (char *)par->sym_name, sizeof(target->entry.sid.name));
}
} else {
if (par->sym_name[0] &&
(find_target_symname(par->list_kind, par->sym_name) ||
find_portal_name(par->sym_name))) {
rsp->status = ISCSID_STATUS_DUPLICATE_NAME;
return;
}
if (par->list_kind == SEND_TARGETS_LIST)
target = create_send_target(par->TargetName, &par->portal[0]);
else
target = create_target(par->TargetName);
if (target == NULL) {
rsp->status = ISCSID_STATUS_NO_RESOURCES;
return;
}
num = 0;
strlcpy((char *)target->entry.sid.name, (char *)par->sym_name,
sizeof(target->entry.sid.name));
}
rsp = make_rsp(sizeof(*res) + (par->num_portals * sizeof(uint32_t)),
prsp, prsp_temp);
if (rsp == NULL)
return;
res = (iscsid_add_target_rsp_t *)(void *)rsp->parameter;
res->target_id = target->entry.sid.id;
if (!num) {
TAILQ_INSERT_TAIL(&list[par->list_kind].list, &target->entry,
link);
list[par->list_kind].num_entries++;
}
if (par->list_kind == SEND_TARGETS_LIST) {
res->portal_id[0] = target->entry.sid.id;
res->num_portals = 1;
} else {
for (i = 0; i < (int)par->num_portals; i++) {
portal = create_portal(target, &par->portal[i],
PORTAL_TYPE_STATIC,
target->entry.sid.id);
if (portal == NULL) {
rsp->status = ISCSID_STATUS_NO_RESOURCES;
break;
}
res->portal_id[i] = portal->entry.sid.id;
}
res->num_portals = i;
}
DEB(9, ("AddTarget returns"));
}
target_t *
add_discovered_target(uint8_t * TargetName, iscsi_portal_address_t * addr,
iscsi_portal_types_t dtype, uint32_t did)
{
target_t *target;
portal_t *portal;
DEB(9, ("Add Discovered Target, name %s, addr %s",
TargetName, addr->address));
if ((target = find_TargetName(TARGET_LIST, TargetName)) == NULL) {
if ((target = create_target(TargetName)) == NULL) {
return NULL;
}
portal = create_portal(target, addr, dtype, did);
if (portal == NULL) {
free(target);
return NULL;
}
TAILQ_INSERT_TAIL(&list[TARGET_LIST].list, &target->entry, link);
list[TARGET_LIST].num_entries++;
} else if ((portal = create_portal(target, addr, dtype, did)) == NULL) {
return NULL;
}
portal->portaltype = dtype;
return target;
}
uint32_t
set_target_options(iscsid_get_set_target_options_t * par)
{
target_t *target;
if ((target = find_target(par->list_kind, &par->target_id)) == NULL)
return ISCSID_STATUS_INVALID_TARGET_ID;
target->options = *par;
return ISCSID_STATUS_SUCCESS;
}
uint32_t
set_target_auth(iscsid_set_target_authentication_req_t * par)
{
target_t *target;
if ((target = find_target(par->list_kind, &par->target_id)) == NULL) {
return ISCSID_STATUS_INVALID_TARGET_ID;
}
target->auth = *par;
return ISCSID_STATUS_SUCCESS;
}
void
get_target_info(iscsid_list_id_t *par, iscsid_response_t **prsp, int *prsp_temp)
{
iscsid_get_target_rsp_t *res;
iscsid_response_t *rsp = *prsp;
uint32_t *idp;
target_t *target;
portal_group_t *cgroup;
portal_t *curr = NULL;
int num = 1;
DEB(10, ("get_target_info, id %d", par->id.id));
if ((target = find_target(par->list_kind, &par->id)) == NULL) {
if (par->list_kind == SEND_TARGETS_LIST ||
(curr = find_portal(&par->id)) == NULL) {
rsp->status = ISCSID_STATUS_INVALID_TARGET_ID;
return;
}
target = curr->target;
} else if (par->list_kind != SEND_TARGETS_LIST) {
num = target->num_portals;
}
rsp = make_rsp(sizeof(*res) + (num - 1) * sizeof(uint32_t),
prsp, prsp_temp);
if (rsp == NULL)
return;
res = (iscsid_get_target_rsp_t *)(void *)rsp->parameter;
res->target_id = target->entry.sid;
strlcpy((char *)res->TargetName, (char *)target->TargetName,
sizeof(res->TargetName));
strlcpy((char *)res->TargetAlias, (char *)target->TargetAlias,
sizeof(res->TargetAlias));
res->num_portals = num;
idp = res->portal;
if (curr) {
*idp = curr->entry.sid.id;
} else if (par->list_kind != SEND_TARGETS_LIST) {
TAILQ_FOREACH(cgroup, &target->group_list, groups)
TAILQ_FOREACH(curr, &cgroup->portals, group_list)
* idp++ = curr->entry.sid.id;
} else
*idp = target->entry.sid.id;
}
void
add_portal(iscsid_add_portal_req_t *par, iscsid_response_t **prsp,
int *prsp_temp)
{
iscsid_add_portal_rsp_t *res;
iscsid_response_t *rsp = *prsp;
target_t *target;
portal_t *portal;
DEB(9, ("Add portal: target %d (%s), symname %s, addr %s",
par->target_id.id, par->target_id.name,
par->sym_name, par->portal.address));
if ((target = find_target(TARGET_LIST, &par->target_id)) == NULL) {
rsp->status = ISCSID_STATUS_INVALID_TARGET_ID;
return;
}
if (par->sym_name[0] &&
(find_target_symname(TARGET_LIST, par->sym_name) ||
find_portal_name(par->sym_name))) {
rsp->status = ISCSID_STATUS_DUPLICATE_NAME;
return;
}
portal = create_portal(target, &par->portal, PORTAL_TYPE_STATIC,
target->entry.sid.id);
if (portal == NULL) {
rsp->status = ISCSID_STATUS_NO_RESOURCES;
return;
}
if (par->sym_name[0]) {
strlcpy((char *)portal->entry.sid.name, (char *)par->sym_name,
sizeof(portal->entry.sid.name));
}
portal->options = par->options;
rsp = make_rsp(sizeof(*res), prsp, prsp_temp);
if (rsp == NULL)
return;
#if 0
res->target_id = target->entry.sid;
res->portal_id = portal->entry.sid;
#endif
DEB(9, ("AddPortal success (id %d)", portal->entry.sid.id));
}
void
get_portal_info(iscsid_list_id_t *par, iscsid_response_t **prsp, int *prsp_temp)
{
iscsid_get_portal_rsp_t *res;
iscsid_response_t *rsp = *prsp;
portal_t *portal = NULL;
send_target_t *starg = NULL;
int err;
DEB(10, ("get_portal_info, id %d", par->id.id));
if (par->list_kind == SEND_TARGETS_LIST)
err = ((starg = (send_target_t *)(void *)find_target(par->list_kind,
&par->id)) == NULL);
else
err = ((portal = find_portal(&par->id)) == NULL);
if (err) {
rsp->status = ISCSID_STATUS_INVALID_PORTAL_ID;
return;
}
rsp = make_rsp(sizeof(*res), prsp, prsp_temp);
if (rsp == NULL)
return;
res = (iscsid_get_portal_rsp_t *)(void *)rsp->parameter;
if (par->list_kind == SEND_TARGETS_LIST) {
res->target_id = starg->entry.sid;
res->portal_id = starg->entry.sid;
res->portal = starg->addr;
} else {
res->target_id = portal->target->entry.sid;
res->portal_id = portal->entry.sid;
res->portal = portal->addr;
}
}
uint32_t
remove_target(iscsid_list_id_t * par)
{
target_t *target = NULL;
portal_t *portal = NULL;
send_target_t *starg = NULL;
int err;
DEB(9, ("remove_target, id %d", par->id.id));
if (par->list_kind == SEND_TARGETS_LIST) {
err = ((starg = (send_target_t *)(void *)find_target(par->list_kind,
&par->id)) == NULL);
if (!err) {
delete_send_target(starg);
}
} else if (par->list_kind == PORTAL_LIST) {
err = ((portal = find_portal(&par->id)) == NULL);
if (!err) {
delete_portal(portal, FALSE);
}
} else {
target = find_target(par->list_kind, &par->id);
err = (target == NULL);
if (!err) {
delete_target(target);
}
}
return err ? ISCSID_STATUS_INVALID_PORTAL_ID : ISCSID_STATUS_SUCCESS;
}
static int
cl_get_address(iscsi_portal_address_t * portal, char *str)
{
char *sp, *sp2;
int val;
for (sp = str + 1, val = 0; *sp && (*sp != ':' || val); sp++) {
if (*sp == '[')
val = 1;
else if (*sp == ']')
val = 0;
}
if (*sp) {
for (sp2 = sp + 1; *sp2 && *sp2 != ':'; sp2++);
if (!*sp2) {
*sp++ = '\0';
if (sscanf(sp, "%d", &val) != 1)
return 0;
if (val < 0 || val > 0xffff)
return 0;
portal->port = (uint16_t) val;
}
for (; isdigit((unsigned char)*sp); sp++) {
}
if (*sp && *sp != ',')
return 0;
} else
for (sp = str + 1; *sp && *sp != ','; sp++);
if (*sp) {
if (sscanf(sp + 1, "%d", &val) != 1)
return 0;
if (val < 0 || val > 0xffff)
return 0;
portal->group_tag = (uint16_t) val;
*sp = '\0';
}
if (strlen(str) >= sizeof(portal->address))
return 0;
strlcpy((char *)portal->address, str, sizeof(portal->address));
return 1;
}
static uint32_t
refresh_send_target(uint32_t id)
{
uint8_t *response_buffer = NULL;
uint32_t response_size;
uint32_t ret;
uint8_t *TargetName;
iscsi_portal_address_t addr;
uint8_t *tp, *sp;
generic_entry_t *curr;
generic_entry_t *next;
send_target_t *sendtarg;
int rc;
TAILQ_FOREACH(curr, &list[PORTAL_LIST].list, link) {
portal_t *p = (void *)curr;
if (p->portaltype == PORTAL_TYPE_SENDTARGET &&
p->discoveryid == id) {
p->portaltype = PORTAL_TYPE_REFRESHING;
}
}
if ((ret = send_targets(id, &response_buffer, &response_size)) == 0) {
tp = response_buffer;
while (*tp) {
if (strncmp((char *)tp, "TargetName=", 11) != 0) {
DEBOUT(("Response not TargetName <%s>", tp));
break;
}
tp += 11;
TargetName = tp;
while (*tp++) {
}
rc = -1;
while (*tp && strncmp((char *)tp, "TargetAddress=", 14) == 0) {
tp += 14;
sp = tp;
while (*tp++) {
}
rc = cl_get_address(&addr, (char *)sp);
if (rc) {
add_discovered_target(TargetName,
&addr, PORTAL_TYPE_SENDTARGET,
id);
} else {
DEBOUT(("Syntax error in returned target address <%s>", sp));
break;
}
}
if (rc == -1) {
sendtarg = find_send_target_id(id);
if (sendtarg != NULL) {
add_discovered_target(TargetName,
&sendtarg->addr,
PORTAL_TYPE_SENDTARGET, id);
}
}
}
}
for (curr = TAILQ_FIRST(&list[PORTAL_LIST].list); curr != NULL;
curr = next) {
portal_t *p = (void *)curr;
next = TAILQ_NEXT(curr, link);
if (p->portaltype == PORTAL_TYPE_REFRESHING)
delete_portal(p, TRUE);
}
if (response_buffer != NULL)
free(response_buffer);
return ret;
}
static void
cleanup_orphans(iscsi_portal_types_t type)
{
generic_entry_t *curr;
generic_entry_t *next;
for (curr = TAILQ_FIRST(&list[PORTAL_LIST].list); curr != NULL;
curr = next) {
portal_t *p = (void *)curr;
next = TAILQ_NEXT(curr, link);
if (p->portaltype == type && p->discoveryid == 0) {
delete_portal(p, TRUE);
}
}
}
uint32_t
refresh_targets(iscsid_refresh_req_t * par)
{
uint32_t t;
uint32_t rc, retval;
generic_entry_t *curr;
retval = ISCSID_STATUS_NO_TARGETS_FOUND;
switch (par->kind) {
case TARGET_LIST:
break;
case SEND_TARGETS_LIST:
DEB(9, ("Refresh Send Targets List - num_ids = %d",
par->num_ids));
if (par->num_ids) {
for (t = 0; t < par->num_ids; t++) {
rc = refresh_send_target(par->id[t]);
if (rc == 0) {
retval = ISCSID_STATUS_SUCCESS;
}
}
} else {
cleanup_orphans(PORTAL_TYPE_SENDTARGET);
TAILQ_FOREACH(curr, &list[SEND_TARGETS_LIST].list, link)
if ((rc = refresh_send_target(curr->sid.id)) == 0)
retval = ISCSID_STATUS_SUCCESS;
}
return retval;
#ifndef ISCSI_MINIMAL
case ISNS_LIST:
DEB(9, ("Refresh iSNS List - num_ids = %d", par->num_ids));
if (par->num_ids) {
for (t = 0; t < par->num_ids; t++)
if ((rc = refresh_isns_server(par->id[t])) == 0)
retval = ISCSI_STATUS_SUCCESS;
} else {
cleanup_orphans(PORTAL_TYPE_ISNS);
TAILQ_FOREACH(curr, &list[ISNS_LIST].list, link)
if ((rc = refresh_isns_server(curr->sid.id)) == 0)
retval = ISCSI_STATUS_SUCCESS;
}
return retval;
#endif
default:
break;
}
return ISCSID_STATUS_PARAMETER_INVALID;
}