/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ #include #if defined(_KERNEL) #include #include #include #include #include #include #else #include #include #include #include #include #include #include #include #endif #include #include #include void * iscsit_zalloc(size_t size) { #if defined(_KERNEL) return (kmem_zalloc(size, KM_SLEEP)); #else return (calloc(1, size)); #endif } void iscsit_free(void *buf, size_t size) /* ARGSUSED */ { #if defined(_KERNEL) kmem_free(buf, size); #else free(buf); #endif } /* * default_port should be the port to be used, if not specified * as part of the supplied string 'arg'. */ #define NI_MAXHOST 1025 #define NI_MAXSERV 32 struct sockaddr_storage * it_common_convert_sa(char *arg, struct sockaddr_storage *buf, uint32_t default_port) { /* Why does addrbuf need to be this big!??! XXX */ char addrbuf[NI_MAXHOST + NI_MAXSERV + 1]; char *addr_str; char *port_str; #ifndef _KERNEL char *errchr; #endif long tmp_port = 0; sa_family_t af; struct sockaddr_in *sin; struct sockaddr_in6 *sin6; struct sockaddr_storage *sa = buf; if (!arg || !buf) { return (NULL); } bzero(buf, sizeof (struct sockaddr_storage)); /* don't modify the passed-in string */ (void) strlcpy(addrbuf, arg, sizeof (addrbuf)); addr_str = addrbuf; if (*addr_str == '[') { /* * An IPv6 address must be inside square brackets */ port_str = strchr(addr_str, ']'); if (!port_str) { /* No closing bracket */ return (NULL); } /* strip off the square brackets so we can convert */ addr_str++; *port_str = '\0'; port_str++; if (*port_str == ':') { /* TCP port to follow */ port_str++; } else if (*port_str == '\0') { /* No port specified */ port_str = NULL; } else { /* malformed */ return (NULL); } af = AF_INET6; } else { port_str = strchr(addr_str, ':'); if (port_str) { *port_str = '\0'; port_str++; } af = AF_INET; } if (port_str) { #if defined(_KERNEL) if (ddi_strtol(port_str, NULL, 10, &tmp_port) != 0) { return (NULL); } #else tmp_port = strtol(port_str, &errchr, 10); #endif if (tmp_port < 0 || tmp_port > 65535) { return (NULL); } } else { tmp_port = default_port; } sa->ss_family = af; sin = (struct sockaddr_in *)sa; if (af == AF_INET) { if (inet_pton(af, addr_str, (void *)&(sin->sin_addr.s_addr)) != 1) { return (NULL); } sin->sin_port = htons(tmp_port); } else { sin6 = (struct sockaddr_in6 *)sa; if (inet_pton(af, addr_str, (void *)&(sin6->sin6_addr.s6_addr)) != 1) { return (NULL); } sin6->sin6_port = htons(tmp_port); } /* successful */ return (sa); } /* Functions to convert iSCSI target structures to/from nvlists. */ #ifndef _KERNEL int it_config_to_nv(it_config_t *cfg, nvlist_t **nvl) { int ret; nvlist_t *nv; nvlist_t *lnv = NULL; if (!nvl) { return (EINVAL); } *nvl = NULL; ret = nvlist_alloc(&nv, NV_UNIQUE_NAME_TYPE, 0); if (ret != 0) { return (ret); } /* if there's no config, store an empty list */ if (!cfg) { *nvl = nv; return (0); } ret = nvlist_add_uint32(nv, "cfgVersion", cfg->config_version); if (ret == 0) { ret = it_tgtlist_to_nv(cfg->config_tgt_list, &lnv); } if ((ret == 0) && (lnv != NULL)) { ret = nvlist_add_nvlist(nv, "targetList", lnv); nvlist_free(lnv); lnv = NULL; } if (ret == 0) { ret = it_tpglist_to_nv(cfg->config_tpg_list, &lnv); } if ((ret == 0) && (lnv != NULL)) { ret = nvlist_add_nvlist(nv, "tpgList", lnv); nvlist_free(lnv); lnv = NULL; } if (ret == 0) { ret = it_inilist_to_nv(cfg->config_ini_list, &lnv); } if ((ret == 0) && (lnv != NULL)) { ret = nvlist_add_nvlist(nv, "iniList", lnv); nvlist_free(lnv); lnv = NULL; } if (ret == 0) { ret = nvlist_add_nvlist(nv, "globalProperties", cfg->config_global_properties); } if (ret == 0) { *nvl = nv; } else { nvlist_free(nv); } return (ret); } #endif /* !_KERNEL */ /* * nvlist version of config is 3 list-of-list, + 1 proplist. arrays * are interesting, but lists-of-lists are more useful when doing * individual lookups when we later add support for it. Also, no * need to store name in individual struct representation. */ int it_nv_to_config(nvlist_t *nvl, it_config_t **cfg) { int ret; uint32_t intval; nvlist_t *listval; it_config_t *tmpcfg; if (!cfg) { return (EINVAL); } /* initialize output */ *cfg = NULL; tmpcfg = iscsit_zalloc(sizeof (it_config_t)); if (tmpcfg == NULL) { return (ENOMEM); } if (!nvl) { /* nothing to decode, but return the empty cfg struct */ ret = nvlist_alloc(&tmpcfg->config_global_properties, NV_UNIQUE_NAME, 0); if (ret != 0) { iscsit_free(tmpcfg, sizeof (it_config_t)); return (ret); } *cfg = tmpcfg; return (0); } ret = nvlist_lookup_uint32(nvl, "cfgVersion", &intval); if (ret != 0) { iscsit_free(tmpcfg, sizeof (it_config_t)); return (ret); } tmpcfg->config_version = intval; ret = nvlist_lookup_nvlist(nvl, "targetList", &listval); if (ret == 0) { /* decode list of it_tgt_t */ ret = it_nv_to_tgtlist(listval, &(tmpcfg->config_tgt_count), &(tmpcfg->config_tgt_list)); } ret = nvlist_lookup_nvlist(nvl, "tpgList", &listval); if (ret == 0) { /* decode list of it_tpg_t */ ret = it_nv_to_tpglist(listval, &(tmpcfg->config_tpg_count), &(tmpcfg->config_tpg_list)); } ret = nvlist_lookup_nvlist(nvl, "iniList", &listval); if (ret == 0) { /* decode list of initiators */ ret = it_nv_to_inilist(listval, &(tmpcfg->config_ini_count), &(tmpcfg->config_ini_list)); } ret = nvlist_lookup_nvlist(nvl, "globalProperties", &listval); if (ret == 0) { /* * don't depend on the original nvlist staying in-scope, * duplicate the nvlist */ ret = nvlist_dup(listval, &(tmpcfg->config_global_properties), 0); } else if (ret == ENOENT) { /* * No global properties defined, make an empty list */ ret = nvlist_alloc(&tmpcfg->config_global_properties, NV_UNIQUE_NAME, 0); } if (ret == 0) { char **isnsArray = NULL; uint32_t numisns = 0; /* * decode the list of iSNS server information to make * references from the kernel simpler. */ if (tmpcfg->config_global_properties) { ret = nvlist_lookup_string_array( tmpcfg->config_global_properties, PROP_ISNS_SERVER, &isnsArray, &numisns); if (ret == 0) { ret = it_array_to_portallist(isnsArray, numisns, ISNS_DEFAULT_SERVER_PORT, &tmpcfg->config_isns_svr_list, &tmpcfg->config_isns_svr_count); } else if (ret == ENOENT) { /* It's OK if we don't have any iSNS servers */ ret = 0; } } } if (ret == 0) { *cfg = tmpcfg; } else { it_config_free_cmn(tmpcfg); } return (ret); } it_tgt_t * it_tgt_lookup(it_config_t *cfg, char *tgt_name) { it_tgt_t *cfg_tgt = NULL; for (cfg_tgt = cfg->config_tgt_list; cfg_tgt != NULL; cfg_tgt = cfg_tgt->tgt_next) { if (strncmp(cfg_tgt->tgt_name, tgt_name, MAX_ISCSI_NODENAMELEN) == 0) { return (cfg_tgt); } } return (NULL); } int it_nv_to_tgtlist(nvlist_t *nvl, uint32_t *count, it_tgt_t **tgtlist) { int ret = 0; it_tgt_t *tgt; it_tgt_t *prev = NULL; nvpair_t *nvp = NULL; nvlist_t *nvt; char *name; if (!tgtlist || !count) { return (EINVAL); } *tgtlist = NULL; *count = 0; if (!nvl) { /* nothing to do */ return (0); } while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { name = nvpair_name(nvp); ret = nvpair_value_nvlist(nvp, &nvt); if (ret != 0) { /* invalid entry? */ continue; } ret = it_nv_to_tgt(nvt, name, &tgt); if (ret != 0) { break; } (*count)++; if (*tgtlist == NULL) { *tgtlist = tgt; } else { prev->tgt_next = tgt; } prev = tgt; } if (ret != 0) { it_tgt_free_cmn(*tgtlist); *tgtlist = NULL; } return (ret); } int it_tgtlist_to_nv(it_tgt_t *tgtlist, nvlist_t **nvl) { int ret; it_tgt_t *tgtp = tgtlist; nvlist_t *pnv = NULL; nvlist_t *tnv; if (!nvl) { return (EINVAL); } if (!tgtlist) { /* nothing to do */ return (0); } /* create the target list if required */ if (*nvl == NULL) { ret = nvlist_alloc(&pnv, NV_UNIQUE_NAME, 0); if (ret != 0) { return (ret); } *nvl = pnv; } while (tgtp) { ret = it_tgt_to_nv(tgtp, &tnv); if (ret != 0) { break; } ret = nvlist_add_nvlist(*nvl, tgtp->tgt_name, tnv); if (ret != 0) { break; } nvlist_free(tnv); tgtp = tgtp->tgt_next; } if (ret != 0) { if (pnv) { nvlist_free(pnv); *nvl = NULL; } } return (ret); } int it_tgt_to_nv(it_tgt_t *tgt, nvlist_t **nvl) { int ret; nvlist_t *tnv = NULL; if (!nvl) { return (EINVAL); } if (!tgt) { /* nothing to do */ return (0); } ret = nvlist_alloc(nvl, NV_UNIQUE_NAME, 0); if (ret != 0) { return (ret); } if (tgt->tgt_properties) { ret = nvlist_add_nvlist(*nvl, "properties", tgt->tgt_properties); } if (ret == 0) { ret = nvlist_add_uint64(*nvl, "generation", tgt->tgt_generation); } if (ret == 0) { ret = it_tpgtlist_to_nv(tgt->tgt_tpgt_list, &tnv); } if ((ret == 0) && tnv) { ret = nvlist_add_nvlist(*nvl, "tpgtList", tnv); nvlist_free(tnv); } if (ret != 0) { nvlist_free(*nvl); *nvl = NULL; } return (ret); } int it_nv_to_tgt(nvlist_t *nvl, char *name, it_tgt_t **tgt) { int ret; it_tgt_t *ttgt; nvlist_t *listval; uint32_t intval; if (!nvl || !tgt || !name) { return (EINVAL); } *tgt = NULL; ttgt = iscsit_zalloc(sizeof (it_tgt_t)); if (!ttgt) { return (ENOMEM); } (void) strlcpy(ttgt->tgt_name, name, sizeof (ttgt->tgt_name)); ret = nvlist_lookup_nvlist(nvl, "properties", &listval); if (ret == 0) { /* duplicate list so it does not go out of context */ ret = nvlist_dup(listval, &(ttgt->tgt_properties), 0); } else if (ret == ENOENT) { ret = 0; } if (ret == 0) { ret = nvlist_lookup_uint64(nvl, "generation", &(ttgt->tgt_generation)); } else if (ret == ENOENT) { ret = 0; } if (ret == 0) { ret = nvlist_lookup_nvlist(nvl, "tpgtList", &listval); } if (ret == 0) { ret = it_nv_to_tpgtlist(listval, &intval, &(ttgt->tgt_tpgt_list)); ttgt->tgt_tpgt_count = intval; } else if (ret == ENOENT) { ret = 0; } if (ret == 0) { *tgt = ttgt; } else { it_tgt_free_cmn(ttgt); } return (ret); } int it_tpgt_to_nv(it_tpgt_t *tpgt, nvlist_t **nvl) { int ret; if (!nvl) { return (EINVAL); } if (!tpgt) { /* nothing to do */ return (0); } ret = nvlist_alloc(nvl, NV_UNIQUE_NAME, 0); if (ret != 0) { return (ret); } ret = nvlist_add_uint16(*nvl, "tag", tpgt->tpgt_tag); if (ret == 0) { ret = nvlist_add_uint64(*nvl, "generation", tpgt->tpgt_generation); } if (ret != 0) { nvlist_free(*nvl); *nvl = NULL; } return (ret); } int it_nv_to_tpgt(nvlist_t *nvl, char *name, it_tpgt_t **tpgt) { int ret; it_tpgt_t *ptr; if (!tpgt || !name) { return (EINVAL); } *tpgt = NULL; if (!nvl) { return (0); } ptr = iscsit_zalloc(sizeof (it_tpgt_t)); if (!ptr) { return (ENOMEM); } (void) strlcpy(ptr->tpgt_tpg_name, name, sizeof (ptr->tpgt_tpg_name)); ret = nvlist_lookup_uint16(nvl, "tag", &(ptr->tpgt_tag)); if (ret == 0) { ret = nvlist_lookup_uint64(nvl, "generation", &(ptr->tpgt_generation)); } if (ret == 0) { *tpgt = ptr; } else { iscsit_free(ptr, sizeof (it_tpgt_t)); } return (ret); } int it_tpgtlist_to_nv(it_tpgt_t *tpgtlist, nvlist_t **nvl) { int ret; nvlist_t *pnv = NULL; nvlist_t *tnv; it_tpgt_t *ptr = tpgtlist; if (!nvl) { return (EINVAL); } if (!tpgtlist) { /* nothing to do */ return (0); } /* create the target list if required */ if (*nvl == NULL) { ret = nvlist_alloc(&pnv, NV_UNIQUE_NAME, 0); if (ret != 0) { return (ret); } *nvl = pnv; } while (ptr) { ret = it_tpgt_to_nv(ptr, &tnv); if (ret != 0) { break; } ret = nvlist_add_nvlist(*nvl, ptr->tpgt_tpg_name, tnv); if (ret != 0) { break; } nvlist_free(tnv); ptr = ptr->tpgt_next; } if (ret != 0) { if (pnv) { nvlist_free(pnv); *nvl = NULL; } } return (ret); } int it_nv_to_tpgtlist(nvlist_t *nvl, uint32_t *count, it_tpgt_t **tpgtlist) { int ret = 0; it_tpgt_t *tpgt; it_tpgt_t *prev = NULL; nvpair_t *nvp = NULL; nvlist_t *nvt; char *name; if (!tpgtlist || !count) { return (EINVAL); } *tpgtlist = NULL; *count = 0; if (!nvl) { /* nothing to do */ return (0); } while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { name = nvpair_name(nvp); ret = nvpair_value_nvlist(nvp, &nvt); if (ret != 0) { /* invalid entry? */ continue; } ret = it_nv_to_tpgt(nvt, name, &tpgt); if (ret != 0) { break; } (*count)++; if (*tpgtlist == NULL) { *tpgtlist = tpgt; } else { prev->tpgt_next = tpgt; } prev = tpgt; } if (ret != 0) { it_tpgt_free_cmn(*tpgtlist); *tpgtlist = NULL; } return (ret); } #ifndef _KERNEL int it_tpg_to_nv(it_tpg_t *tpg, nvlist_t **nvl) { int ret; char **portalArray = NULL; int i; it_portal_t *ptr; if (!nvl) { return (EINVAL); } if (!tpg) { /* nothing to do */ return (0); } ret = nvlist_alloc(nvl, NV_UNIQUE_NAME, 0); if (ret != 0) { return (ret); } ret = nvlist_add_uint64(*nvl, "generation", tpg->tpg_generation); if ((ret == 0) && tpg->tpg_portal_list) { /* add the portals */ portalArray = iscsit_zalloc(tpg->tpg_portal_count * sizeof (it_portal_t)); if (portalArray == NULL) { nvlist_free(*nvl); *nvl = NULL; return (ENOMEM); } i = 0; ptr = tpg->tpg_portal_list; while (ptr && (i < tpg->tpg_portal_count)) { ret = sockaddr_to_str(&(ptr->portal_addr), &(portalArray[i])); if (ret != 0) { break; } ptr = ptr->portal_next; i++; } } if ((ret == 0) && portalArray) { ret = nvlist_add_string_array(*nvl, "portalList", portalArray, i); } if (portalArray) { while (--i >= 0) { if (portalArray[i]) { iscsit_free(portalArray[i], strlen(portalArray[i] + 1)); } } iscsit_free(portalArray, tpg->tpg_portal_count * sizeof (it_portal_t)); } if (ret != 0) { nvlist_free(*nvl); *nvl = NULL; } return (ret); } #endif /* !_KERNEL */ int it_nv_to_tpg(nvlist_t *nvl, char *name, it_tpg_t **tpg) { int ret; it_tpg_t *ptpg; char **portalArray = NULL; uint32_t count = 0; if (!name || !tpg) { return (EINVAL); } *tpg = NULL; ptpg = iscsit_zalloc(sizeof (it_tpg_t)); if (ptpg == NULL) { return (ENOMEM); } (void) strlcpy(ptpg->tpg_name, name, sizeof (ptpg->tpg_name)); ret = nvlist_lookup_uint64(nvl, "generation", &(ptpg->tpg_generation)); if (ret == 0) { ret = nvlist_lookup_string_array(nvl, "portalList", &portalArray, &count); } if (ret == 0) { /* set the portals */ ret = it_array_to_portallist(portalArray, count, ISCSI_LISTEN_PORT, &ptpg->tpg_portal_list, &ptpg->tpg_portal_count); } else if (ret == ENOENT) { ret = 0; } if (ret == 0) { *tpg = ptpg; } else { it_tpg_free_cmn(ptpg); } return (ret); } #ifndef _KERNEL int it_tpglist_to_nv(it_tpg_t *tpglist, nvlist_t **nvl) { int ret; nvlist_t *pnv = NULL; nvlist_t *tnv; it_tpg_t *ptr = tpglist; if (!nvl) { return (EINVAL); } if (!tpglist) { /* nothing to do */ return (0); } /* create the target portal group list if required */ if (*nvl == NULL) { ret = nvlist_alloc(&pnv, NV_UNIQUE_NAME, 0); if (ret != 0) { return (ret); } *nvl = pnv; } while (ptr) { ret = it_tpg_to_nv(ptr, &tnv); if (ret != 0) { break; } ret = nvlist_add_nvlist(*nvl, ptr->tpg_name, tnv); if (ret != 0) { break; } nvlist_free(tnv); ptr = ptr->tpg_next; } if (ret != 0) { if (pnv) { nvlist_free(pnv); *nvl = NULL; } } return (ret); } #endif /* !_KERNEL */ it_tpg_t * it_tpg_lookup(it_config_t *cfg, char *tpg_name) { it_tpg_t *cfg_tpg = NULL; for (cfg_tpg = cfg->config_tpg_list; cfg_tpg != NULL; cfg_tpg = cfg_tpg->tpg_next) { if (strncmp(&cfg_tpg->tpg_name[0], tpg_name, MAX_TPG_NAMELEN) == 0) { return (cfg_tpg); } } return (NULL); } int it_sa_compare(struct sockaddr_storage *sa1, struct sockaddr_storage *sa2) { struct sockaddr_in *sin1, *sin2; struct sockaddr_in6 *sin6_1, *sin6_2; /* * XXX - should we check here for IPv4 addrs mapped to v6? * see also iscsit_is_v4_mapped in iscsit_login.c */ if (sa1->ss_family != sa2->ss_family) { return (1); } /* * sockaddr_in has padding which may not be initialized. * be more specific in the comparison, and don't trust the * caller has fully initialized the structure. */ if (sa1->ss_family == AF_INET) { sin1 = (struct sockaddr_in *)sa1; sin2 = (struct sockaddr_in *)sa2; if ((bcmp(&sin1->sin_addr, &sin2->sin_addr, sizeof (struct in_addr)) == 0) && (sin1->sin_port == sin2->sin_port)) { return (0); } } else if (sa1->ss_family == AF_INET6) { sin6_1 = (struct sockaddr_in6 *)sa1; sin6_2 = (struct sockaddr_in6 *)sa2; if (bcmp(sin6_1, sin6_2, sizeof (struct sockaddr_in6)) == 0) { return (0); } } return (1); } it_portal_t * it_portal_lookup(it_tpg_t *tpg, struct sockaddr_storage *sa) { it_portal_t *cfg_portal; for (cfg_portal = tpg->tpg_portal_list; cfg_portal != NULL; cfg_portal = cfg_portal->portal_next) { if (it_sa_compare(sa, &cfg_portal->portal_addr) == 0) return (cfg_portal); } return (NULL); } it_portal_t * it_sns_svr_lookup(it_config_t *cfg, struct sockaddr_storage *sa) { it_portal_t *cfg_portal; for (cfg_portal = cfg->config_isns_svr_list; cfg_portal != NULL; cfg_portal = cfg_portal->portal_next) { if (it_sa_compare(sa, &cfg_portal->portal_addr) == 0) return (cfg_portal); } return (NULL); } int it_nv_to_tpglist(nvlist_t *nvl, uint32_t *count, it_tpg_t **tpglist) { int ret = 0; it_tpg_t *tpg; it_tpg_t *prev = NULL; nvpair_t *nvp = NULL; nvlist_t *nvt; char *name; if (!tpglist || !count) { return (EINVAL); } *tpglist = NULL; *count = 0; if (!nvl) { /* nothing to do */ return (0); } while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { name = nvpair_name(nvp); ret = nvpair_value_nvlist(nvp, &nvt); if (ret != 0) { /* invalid entry? */ continue; } ret = it_nv_to_tpg(nvt, name, &tpg); if (ret != 0) { break; } (*count)++; if (*tpglist == NULL) { *tpglist = tpg; } else { prev->tpg_next = tpg; } prev = tpg; } if (ret != 0) { it_tpg_free_cmn(*tpglist); *tpglist = NULL; } return (ret); } int it_ini_to_nv(it_ini_t *ini, nvlist_t **nvl) { int ret; if (!nvl) { return (EINVAL); } if (!ini) { return (0); } ret = nvlist_alloc(nvl, NV_UNIQUE_NAME, 0); if (ret != 0) { return (ret); } if (ini->ini_properties) { ret = nvlist_add_nvlist(*nvl, "properties", ini->ini_properties); } if (ret == 0) { ret = nvlist_add_uint64(*nvl, "generation", ini->ini_generation); } else if (ret == ENOENT) { ret = 0; } if (ret != 0) { nvlist_free(*nvl); *nvl = NULL; } return (ret); } int it_nv_to_ini(nvlist_t *nvl, char *name, it_ini_t **ini) { int ret; it_ini_t *inip; nvlist_t *listval; if (!name || !ini) { return (EINVAL); } *ini = NULL; if (!nvl) { return (0); } inip = iscsit_zalloc(sizeof (it_ini_t)); if (!inip) { return (ENOMEM); } (void) strlcpy(inip->ini_name, name, sizeof (inip->ini_name)); ret = nvlist_lookup_nvlist(nvl, "properties", &listval); if (ret == 0) { ret = nvlist_dup(listval, &(inip->ini_properties), 0); } else if (ret == ENOENT) { ret = 0; } if (ret == 0) { ret = nvlist_lookup_uint64(nvl, "generation", &(inip->ini_generation)); } if (ret == 0) { *ini = inip; } else { it_ini_free_cmn(inip); } return (ret); } int it_inilist_to_nv(it_ini_t *inilist, nvlist_t **nvl) { int ret; nvlist_t *pnv = NULL; nvlist_t *tnv; it_ini_t *ptr = inilist; if (!nvl) { return (EINVAL); } if (!inilist) { return (0); } /* create the target list if required */ if (*nvl == NULL) { ret = nvlist_alloc(&pnv, NV_UNIQUE_NAME, 0); if (ret != 0) { return (ret); } *nvl = pnv; } while (ptr) { ret = it_ini_to_nv(ptr, &tnv); if (ret != 0) { break; } ret = nvlist_add_nvlist(*nvl, ptr->ini_name, tnv); if (ret != 0) { break; } nvlist_free(tnv); ptr = ptr->ini_next; } if (ret != 0) { if (pnv) { nvlist_free(pnv); *nvl = NULL; } } return (ret); } int it_nv_to_inilist(nvlist_t *nvl, uint32_t *count, it_ini_t **inilist) { int ret = 0; it_ini_t *inip; it_ini_t *prev = NULL; nvpair_t *nvp = NULL; nvlist_t *nvt; char *name; if (!inilist || !count) { return (EINVAL); } *inilist = NULL; *count = 0; if (!nvl) { /* nothing to do */ return (0); } while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { name = nvpair_name(nvp); ret = nvpair_value_nvlist(nvp, &nvt); if (ret != 0) { /* invalid entry? */ continue; } ret = it_nv_to_ini(nvt, name, &inip); if (ret != 0) { break; } (*count)++; if (*inilist == NULL) { *inilist = inip; } else { prev->ini_next = inip; } prev = inip; } if (ret != 0) { it_ini_free_cmn(*inilist); *inilist = NULL; } return (ret); } /* * Convert a sockaddr to the string representation, suitable for * storing in an nvlist or printing out in a list. */ #ifndef _KERNEL int sockaddr_to_str(struct sockaddr_storage *sa, char **addr) { int ret; char buf[INET6_ADDRSTRLEN + 7]; /* addr : port */ char pbuf[7]; const char *bufp; struct sockaddr_in *sin; struct sockaddr_in6 *sin6; uint16_t port; if (!sa || !addr) { return (EINVAL); } buf[0] = '\0'; if (sa->ss_family == AF_INET) { sin = (struct sockaddr_in *)sa; bufp = inet_ntop(AF_INET, (const void *)&(sin->sin_addr.s_addr), buf, sizeof (buf)); if (bufp == NULL) { ret = errno; return (ret); } port = ntohs(sin->sin_port); } else if (sa->ss_family == AF_INET6) { (void) strlcat(buf, "[", sizeof (buf)); sin6 = (struct sockaddr_in6 *)sa; bufp = inet_ntop(AF_INET6, (const void *)&sin6->sin6_addr.s6_addr, &buf[1], (sizeof (buf) - 1)); if (bufp == NULL) { ret = errno; return (ret); } (void) strlcat(buf, "]", sizeof (buf)); port = ntohs(sin6->sin6_port); } else { return (EINVAL); } (void) snprintf(pbuf, sizeof (pbuf), ":%u", port); (void) strlcat(buf, pbuf, sizeof (buf)); *addr = strdup(buf); if (*addr == NULL) { return (ENOMEM); } return (0); } #endif /* !_KERNEL */ int it_array_to_portallist(char **arr, uint32_t count, uint32_t default_port, it_portal_t **portallist, uint32_t *list_count) { int ret = 0; int i; it_portal_t *portal; it_portal_t *prev = NULL; it_portal_t *tmp; if (!arr || !portallist || !list_count) { return (EINVAL); } *list_count = 0; *portallist = NULL; for (i = 0; i < count; i++) { if (!arr[i]) { /* should never happen */ continue; } portal = iscsit_zalloc(sizeof (it_portal_t)); if (!portal) { ret = ENOMEM; break; } if (it_common_convert_sa(arr[i], &(portal->portal_addr), default_port) == NULL) { iscsit_free(portal, sizeof (it_portal_t)); ret = EINVAL; break; } /* make sure no duplicates */ tmp = *portallist; while (tmp) { if (it_sa_compare(&(tmp->portal_addr), &(portal->portal_addr)) == 0) { iscsit_free(portal, sizeof (it_portal_t)); portal = NULL; break; } tmp = tmp->portal_next; } if (!portal) { continue; } /* * The first time through the loop, *portallist == NULL * because we assigned it to NULL above. Subsequently * prev will have been set. Therefor it's OK to put * lint override before prev->portal_next assignment. */ if (*portallist == NULL) { *portallist = portal; } else { prev->portal_next = portal; } prev = portal; (*list_count)++; } return (ret); } /* * Function: it_config_free_cmn() * * Free any resources associated with the it_config_t structure. * * Parameters: * cfg A C representation of the current iSCSI configuration */ void it_config_free_cmn(it_config_t *cfg) { if (!cfg) { return; } if (cfg->config_tgt_list) { it_tgt_free_cmn(cfg->config_tgt_list); } if (cfg->config_tpg_list) { it_tpg_free_cmn(cfg->config_tpg_list); } if (cfg->config_ini_list) { it_ini_free_cmn(cfg->config_ini_list); } if (cfg->config_global_properties) { nvlist_free(cfg->config_global_properties); } if (cfg->config_isns_svr_list) { it_portal_t *pp = cfg->config_isns_svr_list; it_portal_t *pp_next; while (pp) { pp_next = pp->portal_next; iscsit_free(pp, sizeof (it_portal_t)); pp = pp_next; } } iscsit_free(cfg, sizeof (it_config_t)); } /* * Function: it_tgt_free_cmn() * * Frees an it_tgt_t structure. If tgt_next is not NULL, frees * all structures in the list. */ void it_tgt_free_cmn(it_tgt_t *tgt) { it_tgt_t *tgtp = tgt; it_tgt_t *next; if (!tgt) { return; } while (tgtp) { next = tgtp->tgt_next; if (tgtp->tgt_tpgt_list) { it_tpgt_free_cmn(tgtp->tgt_tpgt_list); } if (tgtp->tgt_properties) { nvlist_free(tgtp->tgt_properties); } iscsit_free(tgtp, sizeof (it_tgt_t)); tgtp = next; } } /* * Function: it_tpgt_free_cmn() * * Deallocates resources of an it_tpgt_t structure. If tpgt->next * is not NULL, frees all members of the list. */ void it_tpgt_free_cmn(it_tpgt_t *tpgt) { it_tpgt_t *tpgtp = tpgt; it_tpgt_t *next; if (!tpgt) { return; } while (tpgtp) { next = tpgtp->tpgt_next; iscsit_free(tpgtp, sizeof (it_tpgt_t)); tpgtp = next; } } /* * Function: it_tpg_free_cmn() * * Deallocates resources associated with an it_tpg_t structure. * If tpg->next is not NULL, frees all members of the list. */ void it_tpg_free_cmn(it_tpg_t *tpg) { it_tpg_t *tpgp = tpg; it_tpg_t *next; it_portal_t *portalp; it_portal_t *pnext; while (tpgp) { next = tpgp->tpg_next; portalp = tpgp->tpg_portal_list; while (portalp) { pnext = portalp->portal_next; iscsit_free(portalp, sizeof (it_portal_t)); portalp = pnext; } iscsit_free(tpgp, sizeof (it_tpg_t)); tpgp = next; } } /* * Function: it_ini_free_cmn() * * Deallocates resources of an it_ini_t structure. If ini->next is * not NULL, frees all members of the list. */ void it_ini_free_cmn(it_ini_t *ini) { it_ini_t *inip = ini; it_ini_t *next; if (!ini) { return; } while (inip) { next = inip->ini_next; if (inip->ini_properties) { nvlist_free(inip->ini_properties); } iscsit_free(inip, sizeof (it_ini_t)); inip = next; } }