/*
* 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) 2012, Oracle and/or its affiliates. All rights reserved.
*/
#include <unistd.h>
#include <assert.h>
#include <string.h>
#include <libscf.h>
#include <wchar.h>
#include <widec.h>
#include <libintl.h>
#include <netdb.h>
#include <errno.h>
#include <fcntl.h>
#include <arpa/inet.h>
/* Non-public header files needed to work with the iSCSI driver. */
#include <sys/scsi/adapters/iscsi_door.h>
#include <sys/scsi/adapters/iscsi_if.h>
#include "suri_impl.h"
#include "suri_strings.h"
#define WRN_SURI_LU_INUSE SURIGTEXT("Could not remove one or more " \
"iSCSI discovery addresses because logical unit is in use")
typedef struct suri_iscsi_disc_addr_props
{
IMA_UINT name_count;
IMA_NODE_NAME names[1];
} suri_iscsi_disc_addr_props;
static suri_err_t
open_iscsi_driver(struct suri_handle *sh, int *fd)
{
if ((*fd = open(ISCSI_DRIVER_DEVCTL, O_RDONLY)) == -1) {
suri_err_set_desc(sh, "%s: %s: \"%s\"",
SURIGTEXT("Could not open iSCSI control device"),
strerror(errno), ISCSI_DRIVER_DEVCTL);
return (ESURI_ERR);
}
return (ESURI_OK);
}
/*
* We have only one iSCSI logical HBA in Solaris. Get it.
*/
static suri_err_t
get_lhba(struct suri_handle *sh, IMA_OID *oid)
{
IMA_OID_LIST *lhbaList = NULL;
IMA_STATUS status = IMA_GetLhbaOidList(&lhbaList);
if (!IMA_SUCCESS(status)) {
suri_err_set_desc(sh, "%s",
SURIGTEXT("Could not get list of iSCSI host adaptors"));
return (ESURI_ERR);
}
if ((lhbaList == NULL) || (lhbaList->oidCount == 0)) {
if (lhbaList != NULL)
(void) IMA_FreeMemory(lhbaList);
suri_err_set_desc(sh, "%s",
SURIGTEXT("No iSCSI host adaptor present on the system"));
return (ESURI_NOENT);
}
*oid = lhbaList->oids[0];
(void) IMA_FreeMemory(lhbaList);
return (ESURI_OK);
}
/*
* Enable the send-targets discovery method if not already enabled.
*/
static suri_err_t
suri_enable_discovery_method(struct suri_handle *sh, IMA_OID lhba)
{
IMA_STATUS status;
IMA_DISCOVERY_PROPERTIES dprops;
status = IMA_GetDiscoveryProperties(lhba, &dprops);
if (!IMA_SUCCESS(status)) {
suri_err_set_desc(sh, "%s",
SURIGTEXT("Could not get discovery method properties"));
return (ESURI_ERR);
}
/*
* IMA_SetSendTargetsDiscovery() can take some time to finish, do not
* enable the method unless we know it is disabled.
*/
if (dprops.sendTargetsDiscoveryEnabled == IMA_FALSE) {
status = IMA_SetSendTargetsDiscovery(lhba, IMA_TRUE);
if (!IMA_SUCCESS(status)) {
suri_err_set_desc(sh, "%s",
SURIGTEXT("Could not enable SendTargets discovery "
"method"));
return (ESURI_ERR);
}
}
return (ESURI_OK);
}
/*
* Based on resolved hostname:port provided in 'tgt', add discovery addresses.
* You should check whether all the discovery addresses already exist before
* using this function. See suri_tgt_disc_addrs_exist().
*/
suri_err_t
suri_iscsi_add_disc_addrs(struct suri_handle *sh, struct addrinfo *tgt)
{
suri_err_t ret;
IMA_STATUS status;
IMA_HOST_ID *phid;
IMA_TARGET_ADDRESS tgtaddr;
IMA_OID lhba, addressOid;
suri_iscsi_t *suri_iscsi = SURIH2ISCSI(sh);
boolean_t disc_addr_added = B_FALSE;
struct sockaddr_in *in;
struct sockaddr_in6 *in6;
assert(suri_iscsi->si_prop_hname[0] != '\0');
if ((ret = get_lhba(sh, &lhba)) != ESURI_OK)
return (ret);
if ((ret = suri_enable_discovery_method(sh, lhba)) != ESURI_OK)
return (ret);
(void) memset(&tgtaddr, '\0', sizeof (tgtaddr));
/* Make lines more manageable. */
phid = &tgtaddr.hostnameIpAddress;
/*
* We add discovery addresses for all IP addresses our hostname
* resolved to.
*/
while (tgt != NULL) {
/*
* In an unlikely case we fail in inet_ntop() and still need a
* printable IP it means we failed in IMA_AddDiscoveryAddress()
* which is what is interesting to the caller. So, let's have
* something in the string if we fail to convert it.
*/
char str_ip[INET6_ADDRSTRLEN] = "unknown";
/* iSCSI works over IP only. */
if (tgt->ai_family != AF_INET && tgt->ai_family != AF_INET6) {
tgt = tgt->ai_next;
continue;
}
if (tgt->ai_family == AF_INET) {
/* LINTED alignment */
in = (struct sockaddr_in *)(tgt->ai_addr);
phid->id.ipAddress.ipv4Address = B_TRUE;
(void) memcpy(phid->id.ipAddress.ipAddress,
&in->sin_addr, sizeof (in->sin_addr));
/* See a comment for str_ip above on why we do (void) */
(void) inet_ntop(AF_INET, &in->sin_addr, str_ip,
sizeof (str_ip));
} else {
/* LINTED alignment */
in6 = (struct sockaddr_in6 *)(tgt->ai_addr);
phid->id.ipAddress.ipv4Address = B_FALSE;
(void) memcpy(phid->id.ipAddress.ipAddress,
&in6->sin6_addr, sizeof (in6->sin6_addr));
/* See a comment for str_ip above on why we do (void) */
(void) inet_ntop(AF_INET6, &in6->sin6_addr, str_ip,
sizeof (str_ip));
}
if (suri_iscsi->si_prop_port == 0)
tgtaddr.portNumber = SURI_ISCSI_DEFAULT_PORT;
else
tgtaddr.portNumber = suri_iscsi->si_prop_port;
tgtaddr.hostnameIpAddress.hostnameInUse = IMA_FALSE;
/*
* Adding an existing discovery address is not an issue and
* IMA_STATUS_SUCCESS is returned.
*/
status = IMA_AddDiscoveryAddress(lhba, tgtaddr,
&addressOid);
if (!IMA_SUCCESS(status)) {
suri_err_set_desc(sh, "%s: \"%s\"",
SURIGTEXT("Could not add IP discovery address"),
str_ip);
return (ESURI_ERR);
}
disc_addr_added = B_TRUE;
tgt = tgt->ai_next;
}
/*
* We might have non-AF_INET families in tgt so if there is no
* AF_INET/AF_INET6 address we consider it a resolution problem.
*/
if (disc_addr_added == B_FALSE) {
suri_err_set_desc(sh, "%s: \"%s\"",
SURIGTEXT("Hostname did not resolve to IPv4/IPv6 address"),
suri_iscsi->si_prop_hname);
return (ESURI_ERR);
}
return (ESURI_OK);
}
/*
* Remove a discovery address.
*/
static suri_err_t
suri_iscsi_remove_disc_addr(struct suri_handle *sh, IMA_OID daddr)
{
IMA_STATUS status;
IMA_OID lhba;
suri_err_t ret = ESURI_OK;
if ((ret = get_lhba(sh, &lhba)) != ESURI_OK)
return (ret);
status = IMA_RemoveDiscoveryAddress(daddr);
if (!IMA_SUCCESS(status)) {
if (sh != NULL) {
if (status == IMA_ERROR_LU_IN_USE)
return (ESURI_LU_INUSE);
suri_err_set_desc(sh, "%s",
SURIGTEXT("Cannot remove iSCSI "
"discovery address"));
return (ESURI_ERR);
}
}
return (ESURI_OK);
}
/*
* Remove all discovery addresses based on a resolved hostname and port in the
* addrinfo structure.
*/
suri_err_t
suri_iscsi_remove_disc_addrs(struct suri_handle *sh, struct addrinfo *rh)
{
int i, ret;
IMA_OID lhba;
IMA_STATUS status;
IMA_OID_LIST *daddrs;
struct addrinfo *rh_orig = rh;
int16_t port = SURI_ISCSI_DEFAULT_PORT;
boolean_t inuse_wrn = B_FALSE;
IMA_DISCOVERY_ADDRESS_PROPERTIES props;
suri_iscsi_t *suri_iscsi = SURIH2ISCSI(sh);
assert(rh != NULL);
if ((ret = get_lhba(sh, &lhba)) != ESURI_OK)
return (ret);
/*
* We are quering discovery targets so it is expected that the method
* will be used. Enable it now if disabled.
*/
if ((ret = suri_enable_discovery_method(sh, lhba)) != ESURI_OK)
return (ret);
status = IMA_GetDiscoveryAddressOidList(lhba, &daddrs);
if (!IMA_SUCCESS(status)) {
suri_err_set_desc(sh, "%s", SURIGTEXT("Could not get list of "
"iSCSI discovery addresses"));
return (ESURI_ERR);
}
if (suri_iscsi->si_prop_port != 0)
port = suri_iscsi->si_prop_port;
/*
* Go through a list of existing discovery addresses and try to match
* each of those with one of the addresses that our hostname resolved
* into.
*/
for (i = 0; i < daddrs->oidCount; ++i) {
IMA_HOST_ID hid;
status = IMA_GetDiscoveryAddressProperties(daddrs->oids[i],
&props);
if (!IMA_SUCCESS(status)) {
suri_err_set_desc(sh, "%s",
SURIGTEXT("Could not get iSCSI discovery address "
"properties"));
(void) IMA_FreeMemory(daddrs);
return (ESURI_ERR);
}
hid = props.discoveryAddress.hostnameIpAddress;
/*
* libima(3LIB) does not support hostnames in discovery
* addresses.
*/
for (rh = rh_orig; rh != NULL; rh = rh->ai_next) {
struct sockaddr_in *in;
struct sockaddr_in6 *in6;
if (rh->ai_family != AF_INET &&
rh->ai_family != AF_INET6) {
continue;
}
if (props.discoveryAddress.portNumber != port)
continue;
if (hid.id.ipAddress.ipv4Address == IMA_TRUE) {
/*
* Continue if the discovery address being
* processed is IPv4 but our resolved address is
* IPv6.
*/
if (rh->ai_family == AF_INET6)
continue;
/* LINTED alignment */
in = (struct sockaddr_in *)(rh->ai_addr);
if (memcmp(hid.id.ipAddress.ipAddress,
&in->sin_addr,
sizeof (in->sin_addr)) != 0) {
continue;
}
} else {
/*
* Continue if the discovery address being
* processed is IPv6 but our resolved address is
* IPv4.
*/
if (rh->ai_family == AF_INET)
continue;
/* LINTED alignment */
in6 = (struct sockaddr_in6 *)(rh->ai_addr);
if (memcmp(hid.id.ipAddress.ipAddress,
&in6->sin6_addr,
sizeof (in6->sin6_addr)) != 0) {
continue;
}
}
/* Found a discovery address to be removed. */
ret = suri_iscsi_remove_disc_addr(sh, daddrs->oids[i]);
if (ret == ESURI_LU_INUSE && !inuse_wrn) {
suri_set_warn(sh, "%s", WRN_SURI_LU_INUSE);
/* Warn just once. */
inuse_wrn = B_TRUE;
/* Not considered an error */
ret = ESURI_OK;
}
if (ret != ESURI_OK)
break;
}
}
(void) IMA_FreeMemory(daddrs);
return (ret);
}
/*
* Return a string IP address converted from a binary representation stored in
* discovery address properties. A helper function needed for rich error
* reporting.
*/
static void
get_str_ip_for_disc_addr_props(IMA_DISCOVERY_ADDRESS_PROPERTIES props,
char *saddr, int size)
{
const char *p;
int inet_family = AF_INET6;
IMA_HOST_ID hid = props.discoveryAddress.hostnameIpAddress;
if (hid.id.ipAddress.ipv4Address == IMA_TRUE)
inet_family = AF_INET;
/*
* inet_ntop(3SOCKET) can only fail on an incorrect address family or a
* buffer of an inadequate size. It is an internal error if we hit any
* of those here.
*/
p = inet_ntop(inet_family, hid.id.ipAddress.ipAddress, saddr, size);
assert(p != NULL);
}
/*
* Allocate a buffer for SendTargets inquiry. 'ntgts' is the number of target
* elements memory should be allocated for. If the function fails '*hdr' is
* left intact.
*/
static suri_err_t
allocate_send_targets_buf(struct suri_handle *sh, int ntgts,
IMA_DISCOVERY_ADDRESS_PROPERTIES props, iscsi_sendtgts_list_t **hdr)
{
int stl_size;
IMA_IP_ADDRESS *ipAddr;
iscsi_sendtgts_list_t *tmp_hdr;
assert(ntgts > 0);
assert(hdr != NULL);
stl_size = sizeof (iscsi_sendtgts_list_t) +
((ntgts - 1) * sizeof (iscsi_sendtgts_entry_t));
tmp_hdr = (iscsi_sendtgts_list_t *)calloc(1, stl_size);
if (tmp_hdr == NULL)
return (suri_err_set_static_desc(sh, ESURI_NOMEM));
if (*hdr != NULL)
free(*hdr);
/* Set the header to the newly allocated memory and initialize. */
*hdr = tmp_hdr;
(*hdr)->stl_entry.e_vers = ISCSI_INTERFACE_VERSION;
(*hdr)->stl_in_cnt = ntgts;
ipAddr = &props.discoveryAddress.hostnameIpAddress.id.ipAddress;
if (ipAddr->ipv4Address == IMA_TRUE)
(*hdr)->stl_entry.e_insize = sizeof (struct in_addr);
else
(*hdr)->stl_entry.e_insize = sizeof (struct in6_addr);
(void) memcpy(&(*hdr)->stl_entry.e_u, ipAddr->ipAddress,
sizeof (*hdr)->stl_entry.e_u);
(*hdr)->stl_entry.e_port = props.discoveryAddress.portNumber;
return (ESURI_OK);
}
/*
* Function issues a SendTargets request and returns a list of targets available
* on the iSCSI server. 'props' are properties for one specific discovery
* address which contains the IP address of the iSCSI server we are going to
* send the request to.
*/
#define SENDTGTS_DEFAULT_NUM_TARGETS 10
static suri_err_t
suri_iscsi_send_targets(struct suri_handle *sh,
IMA_DISCOVERY_ADDRESS_PROPERTIES props, suri_iscsi_disc_addr_props **ppList)
{
suri_err_t ret;
int i, ntgts, fd;
iscsi_sendtgts_list_t *stl_hdr = NULL;
if ((ret = open_iscsi_driver(sh, &fd)) != ESURI_OK)
return (ret);
ntgts = SENDTGTS_DEFAULT_NUM_TARGETS;
/*
* Iterate until we can accomodate all the targets available. Each
* additional iteration means that the room allocated was not enough so
* we have to reallocate the memory for the target list and send another
* inquiry.
*/
for (;;) {
int err;
ret = allocate_send_targets_buf(sh, ntgts, props, &stl_hdr);
if (ret != ESURI_OK) {
(void) close(fd);
if (stl_hdr != NULL)
free(stl_hdr);
return (ret);
}
err = ioctl(fd, ISCSI_SENDTGTS_GET, stl_hdr);
/*
* If the discovery address is not accessible or has other
* problems, we get EFAULT. It is not considered an error. We
* cannot control what discovery addresses are configured on the
* system. We return an empty output for this situation.
*/
if (err != 0 && errno == EFAULT) {
stl_hdr->stl_out_cnt = 0;
break;
}
/* We return an error for other issues the driver run into. */
if (err != 0) {
/*
* We do not want get_str_ip_for_disc_addr_props() to
* change the errno value we got from ioctl(2) above.
*/
int saved_errno = errno;
char saddr[INET6_ADDRSTRLEN];
/* Let's have a printable IP address for the message */
get_str_ip_for_disc_addr_props(props, saddr,
sizeof (saddr));
suri_err_set_desc(sh, "%s: %s: \"%s\"",
SURIGTEXT("SendTargets request failed"),
strerror(saved_errno), saddr);
(void) close(fd);
free(stl_hdr);
return (ESURI_ERR);
}
/* If we got all the targets, we are done. */
if (stl_hdr->stl_in_cnt >= stl_hdr->stl_out_cnt)
break;
/* We need to increase our output buffer and try again */
ntgts = stl_hdr->stl_out_cnt;
}
(void) close(fd);
/*
* Allocate return buffer. We allocate on more entry but we need it so
* that we still allocate a structure for an empty output.
*/
*ppList = (suri_iscsi_disc_addr_props *)calloc(1,
sizeof (suri_iscsi_disc_addr_props) +
(stl_hdr->stl_out_cnt) * sizeof (IMA_NODE_NAME));
if (*ppList == NULL) {
free(stl_hdr);
return (suri_err_set_static_desc(sh, ESURI_NOMEM));
}
(*ppList)->name_count = stl_hdr->stl_out_cnt;
for (i = 0; i < stl_hdr->stl_out_cnt; i++) {
(void) mbstowcs((*ppList)->names[i],
(char *)stl_hdr->stl_list[i].ste_name,
IMA_NODE_NAME_LEN);
}
free(stl_hdr);
return (IMA_STATUS_SUCCESS);
}
/*
* Return a list of discovery address OIDs.
*/
suri_err_t
suri_get_discovery_addresses(struct suri_handle *sh, IMA_OID_LIST **daddrs)
{
int ret;
IMA_OID lhba;
IMA_STATUS status;
if ((ret = get_lhba(sh, &lhba)) != ESURI_OK)
return (ret);
status = IMA_GetDiscoveryAddressOidList(lhba, daddrs);
if (!IMA_SUCCESS(status)) {
suri_err_set_desc(sh, "%s", SURIGTEXT("Could not get iSCSI "
"discovery address list"));
return (ESURI_ERR);
}
return (ESURI_OK);
}
/*
* For a given target iqn, fill out a hostname and port properties of the handle
* if the target is present in the output of the SendTargets query for the
* discovery address 'daddr_oid'. If the target is not found, leave the hostname
* and port unset and return ESURI_OK. If the target is found but the IP address
* cannot be resolved, use a string representation of the IP address.
*/
suri_err_t
suri_get_auth_section_for_iscsi_tgt(struct suri_handle *sh, const char *iqn,
IMA_OID daddr_oid)
{
int err, i;
const char *p;
suri_err_t ret;
IMA_HOST_ID hid;
IMA_STATUS status;
IMA_NODE_NAME wiqn;
IMA_DISCOVERY_ADDRESS_PROPERTIES props;
int addr_family;
struct in6_addr in6;
/* Fits both IPv4 and IPv6 address */
char saddr[INET6_ADDRSTRLEN];
char host[NI_MAXHOST];
suri_iscsi_disc_addr_props *pList;
struct addrinfo *resolved, hints = {NULL};
suri_iscsi_t *suri_iscsi = SURIH2ISCSI(sh);
(void) mbstowcs(wiqn, iqn, IMA_NODE_NAME_LEN);
status = IMA_GetDiscoveryAddressProperties(daddr_oid, &props);
if (!IMA_SUCCESS(status)) {
/*
* Note we cannot be more specific and print which discovery
* address it failed for since the IP address is in the
* properties.
*/
suri_err_set_desc(sh, "%s",
SURIGTEXT("Could not get iSCSI discovery address "
"properties"));
return (ESURI_ERR);
}
if ((ret = suri_iscsi_send_targets(sh, props, &pList)) != ESURI_OK)
return (ret);
/* Find our iSCSI target port name (or not) */
for (i = 0; i < pList->name_count; ++i) {
if (wcscmp(wiqn, pList->names[i]) == 0)
break;
}
/* Will have an empty authority section if the target was not found. */
if (i >= pList->name_count) {
free(pList);
return (ESURI_OK);
}
free(pList);
/*
* We found our target port name, let's try to find the hostname for the
* discovery address.
*/
hid = props.discoveryAddress.hostnameIpAddress;
addr_family = AF_INET;
if (hid.id.ipAddress.ipv4Address == IMA_FALSE)
addr_family = AF_INET6;
/*
* inet_ntop can only fail on incorrect address family or a buffer of an
* inadequate size. It is an internal error if we hit any of those here.
*/
p = inet_ntop(addr_family, hid.id.ipAddress.ipAddress,
saddr, INET6_ADDRSTRLEN);
assert(p != NULL);
/*
* Discovery address is an IP address, not a hostname. To resolve it,
* getnameinfo(3SOCKET) is used. However, that function needs sockaddr_t
* structure while discovery addresses are byte arrays. So, we use
* getaddrinfo(3SOCKET) to get a sockaddr_t structure out of a string IP
* address prepared above and then we use the structure to look up the
* hostname (or get back to the string IP address if there is no reverse
* available).
*/
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
if ((err = getaddrinfo(saddr, NULL, &hints, &resolved)) != 0) {
suri_err_set_desc(sh, "%s: %s: \"%s\"",
SURIGTEXT("Cannot convert IP address into its "
"binary representation"), gai_strerror(err),
saddr);
return (ESURI_ERR);
}
/*
* We were "resolving" an IP address and can only have one (the first)
* sockaddr_t structure in the addrinfo list.
*/
if ((err = getnameinfo(resolved->ai_addr, resolved->ai_addrlen,
host, sizeof (host), NULL, 0, 0)) != 0) {
freeaddrinfo(resolved);
if (err == EAI_MEMORY)
return (suri_err_set_static_desc(sh, ESURI_NOMEM));
suri_err_set_desc(sh, "%s: %s: \"%s\"",
SURIGTEXT("Cannot not resolve IP address"),
gai_strerror(err), saddr);
return (ESURI_ERR);
}
freeaddrinfo(resolved);
/*
* We can have an IPv6 discovery address for which there was no reverse.
* As convention requires, we must enclose it in [] in the URI so that
* it does not clash with the ":port" suffix. inet_pton(3SOCKET) will
* tell us if we have an IPv6 address.
*/
err = inet_pton(AF_INET6, host, &in6);
/* Could happen on unknown address family. Not possible here. */
assert(err != -1);
if (err == 1) {
/* We have an IPv6 address, enclose it in brackets */
(void) snprintf(suri_iscsi->si_prop_hname,
sizeof (suri_iscsi->si_prop_hname), "[%s]", host);
} else {
/* Hostname or an IPv4 address */
(void) snprintf(suri_iscsi->si_prop_hname,
sizeof (suri_iscsi->si_prop_hname), "%s", host);
}
suri_iscsi->si_prop_port = props.discoveryAddress.portNumber;
return (ESURI_OK);
}
/*
* Return ESURI_OK if the devname is found in the LUN properties. Return
* ESURI_NOENT if not found and ESURI_ERR if any error is hit.
*
* Devname is a device name stripped of the slice ("c0t0d0", for example).
*/
static int
devname_in_lunlist(struct suri_handle *sh, IMA_OID_LIST *lun_list,
const char *devname, const wchar_t *tgt)
{
int j;
IMA_STATUS status;
IMA_LU_PROPERTIES lprops;
for (j = 0; j < lun_list->oidCount; j++) {
char str[MAXPATHLEN];
status = IMA_GetLuProperties(lun_list->oids[j], &lprops);
if (!IMA_SUCCESS(status)) {
char mbtgt[IMA_NODE_NAME_LEN];
/* lint's directive PRINTFLIKE2 does not accept %S */
(void) wcstombs(mbtgt, tgt, sizeof (mbtgt));
suri_err_set_desc(sh, "%s: \"%s\"",
SURIGTEXT("Cannot get LUN properties for target"),
mbtgt);
return (ESURI_ERR);
}
if (lprops.osDeviceNameValid == IMA_FALSE)
continue;
(void) wcstombs(str, lprops.osDeviceName, sizeof (str));
if (strcmp(devname, get_stripped_devname(str)) == 0)
return (ESURI_OK);
}
return (ESURI_NOENT);
}
/*
* For a given device path 'devp', find an iSCSI target for which the device is
* accessible through. Caller is responsible for freeing the output parameter
* '*tgt'. The device path is supposed to be an existing absolute path for an
* iSCSI device and can be both in /dev/dsk and /dev/rdsk, and can contain any
* slice.
*
* Return ESURI_OK if target is found, ESURI_NOENT if not found, and a specific
* error otherwise.
*/
suri_err_t
suri_get_tgt_name_for_devpath(struct suri_handle *sh, const char *devp,
char **tgt)
{
int i;
char *devcp;
suri_err_t ret;
const char *sdev;
IMA_STATUS status;
IMA_OID lhba, initiator;
IMA_NODE_PROPERTIES props;
IMA_TARGET_PROPERTIES tprops;
IMA_OID_LIST *target_list = NULL, *lun_list = NULL;
assert(tgt != NULL);
if ((ret = get_lhba(sh, &lhba)) != ESURI_OK)
return (ret);
if ((devcp = strdup(devp)) == NULL)
return (suri_err_set_static_desc(sh, ESURI_NOMEM));
sdev = get_stripped_devname(devcp);
/* Get initiator node OID. */
status = IMA_GetSharedNodeOid(&initiator);
if (!IMA_SUCCESS(status)) {
suri_err_set_desc(sh, "%s",
SURIGTEXT("Cannot get iSCSI initiator"));
ret = ESURI_ERR;
goto out;
}
status = IMA_GetNodeProperties(initiator, &props);
if (!IMA_SUCCESS(status)) {
suri_err_set_desc(sh, "%s",
SURIGTEXT("Cannot get iSCSI initiator properties"));
ret = ESURI_ERR;
goto out;
}
status = IMA_GetTargetOidList(lhba, &target_list);
if (!IMA_SUCCESS(status)) {
suri_err_set_desc(sh, "%s",
SURIGTEXT("Cannot get iSCSI target list"));
ret = ESURI_ERR;
goto out;
}
/* Each target can have several logical units accessible through it. */
for (i = 0; i < target_list->oidCount; i++) {
status = IMA_GetTargetProperties(target_list->oids[i], &tprops);
if (!IMA_SUCCESS(status)) {
/*
* We cannot be more specific and print the target name
* since it is in the properties we failed to get.
*/
suri_err_set_desc(sh, "%s", SURIGTEXT("Cannot "
"get properties for iSCSI target"));
ret = ESURI_ERR;
goto out;
}
status = IMA_GetLuOidList(target_list->oids[i], &lun_list);
if (!IMA_SUCCESS(status)) {
char mbtgt[IMA_NODE_NAME_LEN];
/*
* lint's directive PRINTFLIKE2 used for
* suri_err_set_desc() does not accept %S, we must
* convert the name to a multibyte string.
*/
(void) wcstombs(mbtgt, tprops.name, sizeof (mbtgt));
suri_err_set_desc(sh, "%s: \"%s\"", SURIGTEXT("Cannot "
"get logical unit list for target"), mbtgt);
ret = ESURI_ERR;
goto out;
}
if (lun_list->oidCount == 0) {
(void) IMA_FreeMemory(lun_list);
continue;
}
ret = devname_in_lunlist(sh, lun_list, sdev, tprops.name);
if (ret == ESURI_NOENT) {
(void) IMA_FreeMemory(lun_list);
continue;
}
if (ret != ESURI_OK)
goto out;
if ((*tgt = calloc(wcslen(tprops.name), 1)) == NULL) {
ret = suri_err_set_static_desc(sh, ESURI_NOMEM);
goto out;
}
(void) wcstombs(*tgt, tprops.name, IMA_NODE_NAME_LEN);
break;
}
/*
* If target is not found, returned value is from the last
* devname_in_lunlist() call.
*/
out:
if (target_list != NULL)
(void) IMA_FreeMemory(target_list);
if (lun_list != NULL)
(void) IMA_FreeMemory(lun_list);
free(devcp);
return (ret);
}