OpenSolaris_b135/common/iscsi/utils.c

Compare this file to the similar file:
Show the results in this format:

/*
 * 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 2008 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include <sys/types.h>
#include <sys/socket.h>


#ifdef _KERNEL
#include <sys/sunddi.h>
#else
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <ctype.h>
#include <netinet/in.h>
#include <sys/utsname.h>

/*
 * NOTE: This routine is found in libnsl. There's apparently no prototype to
 * be found in any of the header files in /usr/include so defining a prototype
 * here to keep the compiler happy.
 */
int getdomainname(char *, int);

static const char *iqn_template		= "iqn.2004-02.%s";
#endif

#include <sys/scsi/adapters/iscsi_if.h>

typedef struct utils_val_name {
	int	u_val;
	char	*u_name;
} utils_val_name_t;

utils_val_name_t param_names[] = {
	{ ISCSI_LOGIN_PARAM_DATA_SEQUENCE_IN_ORDER, "Sequence In Order"},
	{ ISCSI_LOGIN_PARAM_IMMEDIATE_DATA, "Immediate Data"},
	{ ISCSI_LOGIN_PARAM_INITIAL_R2T, "Inital R2T"},
	{ ISCSI_LOGIN_PARAM_DATA_PDU_IN_ORDER, "Data PDU In Order"},
	{ ISCSI_LOGIN_PARAM_HEADER_DIGEST, "Header Digest"},
	{ ISCSI_LOGIN_PARAM_DATA_DIGEST, "Data Digest"},
	{ ISCSI_LOGIN_PARAM_DEFAULT_TIME_2_RETAIN, "Default Time To Retain"},
	{ ISCSI_LOGIN_PARAM_DEFAULT_TIME_2_WAIT, "Default Time To Wait"},
	{ ISCSI_LOGIN_PARAM_MAX_RECV_DATA_SEGMENT_LENGTH,
	    "Max Recv Data Segment Length"},
	{ ISCSI_LOGIN_PARAM_FIRST_BURST_LENGTH, "First Burst Length"},
	{ ISCSI_LOGIN_PARAM_MAX_BURST_LENGTH, "Max Burst Length"},
	{ ISCSI_LOGIN_PARAM_MAX_CONNECTIONS, "Max Connections"},
	{ ISCSI_LOGIN_PARAM_OUTSTANDING_R2T, "Outstanding R2T"},
	{ ISCSI_LOGIN_PARAM_ERROR_RECOVERY_LEVEL, "Error Recovery Level"},
	{ 0, NULL }
};

/*
 * utils_map_param -- Given a parameter return it's ascii name
 *
 * This routine was created because previously an array contained in order
 * the parameter names. Once or twice the parameters value changed which
 * changed the order, but not the array. To avoid further confusion we'll
 * do a simple lookup. This code is rarely called so it shouldn't be an
 * issue.
 */
char *
utils_map_param(int p)
{
	utils_val_name_t	*pn;

	for (pn = param_names; pn->u_name != NULL; pn++)
		if (pn->u_val == p)
			return (pn->u_name);
	return (NULL);
}

/*
 * prt_bitmap -- print out ascii strings associated with bit numbers.
 */
char *
prt_bitmap(int bitmap, char *str, char *buf, int size)
{
	char	*p		= NULL;
	char	*start		= buf;
	int	do_put		= 0;

	/*
	 * The maximum space required will if the bitmap was all 1's which
	 * would cause the octal characters to be replaced by '|'. So make
	 * sure the buffer has enough space.
	 */
	if (size < strlen(str))
		return ("No room");

	for (p = str; size--; p++) {
		if (*p < 0x20) {

			/*
			 * if we have been putting out stuff add separator
			 */
			if (do_put)
				*buf++ = '|';

			do_put = ((1 << *p) & bitmap);
			bitmap &= ~(1 << *p);

		} else if (do_put)
			*buf++ = *p;
	}

	/* ---- remove the last separator if it was added ---- */
	if ((buf > start) && (*(buf - 1) == '|'))
		buf--;
	*buf = '\0';
	return (start);
}

/*
 * parse_addr_port_tpgt - Used to parse addr, port and tpgt from string
 *
 * This function is used to parse addr, port and tpgt from a string.  Callers
 * of this function are the sendtargets and login redirection code.  The
 * caller must be aware that this function will modify the callers string
 * to insert NULL terminators if required.  Port and TPGT are optional.
 */
boolean_t
parse_addr_port_tpgt(char *in, char **addr, int *type, char **port, char **tpgt)
{
	char	*t_port, *t_tpgt;

	/* default return values if requested */
	if (addr == NULL) {
		return (B_FALSE);
	} else {
		*addr = NULL;
	}
	if (port != NULL) {
		*port = NULL;
	}
	if (tpgt != NULL) {
		*tpgt = NULL;
	}

	/* extract ip or domain name */
	if (*in == '[') {
		/* IPV6 */
		*type = AF_INET6;
		*addr = ++in;
		in = strchr(*addr, ']');
		if (in == NULL)
			return (B_FALSE);
			*in++ = '\0';
	} else {
		/* IPV4 or domainname */
		*type = AF_INET;
		*addr = in;
	}

	/* extract port */
	if (port != NULL) {
		t_port = strchr(in, ':');
		if (t_port != NULL) {
			*t_port++ = '\0';
			*port = in = t_port;
		}
	}

	/* exact tpgt */
	if (tpgt != NULL) {
		t_tpgt = strchr(in, ',');
		if (t_tpgt != NULL) {
			*t_tpgt++ = '\0';
			*tpgt = in = t_tpgt;
		}
	}

	return (B_TRUE);
}

#ifndef _KERNEL
/*
 * []--------------------------------------------------------------[]
 * | reverse_fqdn -- given a fully qualified domain name reverse it |
 * |                                                                |
 * | The routine has the obvious problem that it can only handle a  |
 * | name with 5 or less dots. This needs to be fixed by counting   |
 * | the number of dots in the incoming name, calloc'ing an array   |
 * | of the appropriate size and then handling the pointers.	    |
 * []--------------------------------------------------------------[]
 */
static boolean_t
/* LINTED E_FUNC_ARG_UNUSED for 3rd arg size */
reverse_fqdn(const char *domain, char *buf, int size)
{
	char	*ptrs[5];
	char	*dp;
	char	*dp1;
	char	*p;
	int	v = 4;

	if ((dp = dp1 = malloc(strlen(domain) + 1)) == NULL)
		return (B_FALSE);
	(void) strcpy(dp, domain);
	while ((p = (char *)strchr(dp, '.')) != NULL) {
		*p = '\0';
		if (v < 0) {
			free(dp1);
			return (B_FALSE);
		}
		ptrs[v--] = dp;
		dp = p + 1;
	}
	(void) strcpy(buf, dp);
	for (v++; v < 5; v++) {
		(void) strcat(buf, ".");
		(void) strcat(buf, ptrs[v]);
	}
	free(dp1);
	return (B_TRUE);
}

/*
 * []------------------------------------------------------------------[]
 * | utils_iqn_create -- returns an iqn name for the machine		|
 * |									|
 * | The information found in the iqn is not correct. The year and	|
 * | date should be flexible. Currently this is hardwired to the	|
 * | current year and month of this project.				|
 * []------------------------------------------------------------------[]
 */
boolean_t
utils_iqn_create(char *iqn_buf, int size)
{
	struct utsname	uts_info;
	char		domainname[256];
	char		*temp = NULL;
	char		*p;
	char		*pmet = NULL; /* temp reversed .. get it */
	int		len;
	boolean_t	rval = B_FALSE; /* Default */

	if (uname(&uts_info) == -1) {
		goto out;
	}

	if (getdomainname(domainname, sizeof (domainname))) {
		goto out;
	}

	if ((temp = malloc(strlen(uts_info.nodename) +
	    strlen(domainname) + 2)) == NULL) {
		goto out;
	}

	/*
	 * getdomainname always returns something in the order of
	 * host.domainname so we need to skip over that portion of the
	 * host name because we don't care about it.
	 */
	if ((p = strchr(domainname, '.')) == NULL)
		p = domainname;
	else
		p++;

	/* ---- Create Fully Qualified Domain Name ---- */
	(void) snprintf(temp, strlen(p), "%s.%s", uts_info.nodename, p);

	/* ---- According to the spec, names must be lower case ---- */
	for (p = temp; *p; p++)
		if (isupper(*p))
			*p = tolower(*p);

	len = strlen(temp) + 1;
	if ((pmet = malloc(len)) == NULL) {
		goto out;
	}

	if (reverse_fqdn(temp, pmet, len) == B_FALSE) {
		goto out;
	}

	/*
	 * Now use the template with the reversed domainname to create
	 * an iSCSI name using the IQN format. Only count it a success
	 * if the number of characters formated is less than the buffer
	 * size.
	 */
	if (snprintf(iqn_buf, size, iqn_template, pmet) <= size)
		rval = B_TRUE;
out:
	if (temp)
		free(temp);
	if (pmet)
		free(pmet);

	return (rval);
}
#endif /* !_KERNEL */