OpenSolaris_b135/cmd/iscsi/iscsitadm/helper.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.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

#include <stdlib.h>
#include <stdio.h>
#include <wchar.h>
#include <widec.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#include <libintl.h>
#include <limits.h>
#include <string.h>
#include <strings.h>
#include <syslog.h>
#include <errno.h>
#include <netinet/in.h>
#include <sys/iscsi_protocol.h>
#include <door.h>
#include <sys/scsi/generic/inquiry.h>
#include <sys/mman.h>
#include <sys/filio.h>
#include <libxml/xmlreader.h>
#include <libscf.h>
#include <fcntl.h>

#include <iscsitgt_impl.h>
#include "cmdparse.h"
#include "utility.h"
#include "helper.h"

extern char *cmdName;

static stat_delta_t *stat_head;

/*
 * []----
 * | buffer_xml -- buffer incoming XML response until complete
 * |
 * | Incoming data from target may not be a complete XML message. So,
 * | we need to wait until we've got everything otherwise the XML routines
 * | will generate a parsing error for a short buffer.
 * []----
 */
Boolean_t
buffer_xml(char *s, char **storage, tgt_node_t **np)
{
	tgt_node_t		*node		= NULL;
	xmlTextReaderPtr	r;
	char			*p,
	    *e,
	    *end_tag,
	    hold_ch;

	p = *storage;
	if (s != NULL) {
		if (p == NULL) {
			p = strdup(s);
		} else {
			p = realloc(p, strlen(p) + strlen(s) + 1);
			(void) strcat(p, s);
		}
	}
	if (p == NULL) {
		return (False);
	}

	if (*p != '<') {
		return (False);
	}

	if ((e = strchr(p, '>')) == NULL) {
		return (False);
	}

	/*
	 * The +3 is for the slash, closing tag character and null
	 * For example if p is pointing at a string which starts with
	 * "<foo>...."
	 * p will point at '<' and e will point at '>'. e - p is 4, yet
	 * the tag length is really 5 characters. We will need to create
	 * the end tag which also has a slash and NULL byte.
	 */
	if ((end_tag = malloc(e - p + 3)) == NULL) {
		return (False);
	}

	end_tag[0] = '<';
	end_tag[1] = '/';

	/*
	 * Copy in the tag value and the closing tag character '>'.
	 */
	bcopy(p + 1, &end_tag[2], e - p);

	/*
	 * Add the null byte
	 */
	end_tag[e - p + 2] = '\0';

	/*
	 * Do we have the closing string yet? If not, just return
	 */
	if ((e = strstr(p, end_tag)) == NULL) {
		*storage = p;
		return (False);
	}

	/*
	 * Move past the closing tag and free the end_tag memory
	 */
	e += strlen(end_tag);
	free(end_tag);

	/*
	 * NULL terminate the string and remember to save that character
	 * so that we can restore it later.
	 */
	hold_ch = *e;
	*e = '\0';

	if ((r = (xmlTextReaderPtr)xmlReaderForMemory(p, strlen(p), NULL,
	    NULL, 0)) == NULL)
		return (False);

	while (xmlTextReaderRead(r) == 1) {
		if (tgt_node_process(r, &node) == False)
			break;
	}

	*np = node;

	xmlFreeTextReader(r);

	*e = hold_ch;
	for (; isspace(*e); e++)
		;
	if (*e != '\0') {
		*storage = strdup(e);
	} else
		*storage = NULL;
	free(p);
	return (True);
}

/*
 * Retrieve CHAP secret from input
 */
int
getSecret(char *secret, int *secretLen, int minSecretLen, int maxSecretLen)
{
	char *chapSecret;

	/* XXX Should we prompt for hex or ascii printable input? */

	/* get password */
	chapSecret = getpassphrase(gettext("Enter secret:"));

	if (strlen(chapSecret) > maxSecretLen) {
		(void) fprintf(stderr, "%s: %s\n", cmdName,
		    gettext("secret too long"));
		*secret = NULL;
		return (1);
	}

	if (strlen(chapSecret) < minSecretLen && strlen(chapSecret) != 0) {
		(void) fprintf(stderr, "%s: %s\n", cmdName,
		gettext("secret too short"));
			*secret = NULL;
			return (1);
	}

	(void) strcpy(secret, chapSecret);

	chapSecret = getpassphrase(gettext("Re-enter secret:"));
	if (strcmp(secret, chapSecret) != 0) {
		(void) fprintf(stderr, "%s: %s\n", cmdName,
		    gettext("secret not changed"));
		*secret = NULL;
		return (1);
	}
	*secretLen = strlen(chapSecret);
	return (0);
}

void
iSCSINameCheckStatusDisplay(iSCSINameCheckStatusType status)
{
	switch (status) {
		case iSCSINameLenZero:
			(void) fprintf(stderr, "%s: %s\n",
			    cmdName, gettext("empty iSCSI name."));
			break;
		case iSCSINameLenExceededMax:
			(void) fprintf(stderr, "%s: %s\n", cmdName,
			    gettext("iSCSI name exceeded maximum length."));
			break;
		case iSCSINameUnknownType:
			(void) fprintf(stderr, "%s: %s\n", cmdName,
			    gettext("unknown iSCSI name type."));
			break;
		case iSCSINameIqnFormatError:
			(void) fprintf(stderr, "%s: %s\n", cmdName,
			    gettext("iqn formatting error."));
			break;
		case iSCSINameEUIFormatError:
			(void) fprintf(stderr, "%s: %s\n", cmdName,
			    gettext("eui formatting error."));
			break;
	}
}

/*
 * This helper function could go into a utility module for general use.
 */
int
parseAddress(char *address_port_str,
    uint16_t defaultPort,
    char *address_str,
    size_t address_str_len,
    uint16_t *port,
    boolean_t *isIpv6)
{
	char port_str[64];
	int tmp_port;

	if (address_port_str[0] == '[') {
		/* IPv6 address */
		char *close_bracket_pos;
		close_bracket_pos = strchr(address_port_str, ']');
		if (!close_bracket_pos) {
			syslog(LOG_USER|LOG_DEBUG,
			    "IP address format error: %s\n", address_str);
			return (PARSE_ADDR_MISSING_CLOSING_BRACKET);
		}

		*close_bracket_pos = NULL;
		(void) strlcpy(address_str, &address_port_str[1],
		    address_str_len);

		/* Extract the port number */
		close_bracket_pos++;
		if (*close_bracket_pos == ':') {
			close_bracket_pos++;
			if (*close_bracket_pos != NULL) {
				(void) strlcpy(port_str, close_bracket_pos,
				    64);
				tmp_port = atoi(port_str);
				if (((tmp_port > 0) &&
				    (tmp_port > USHRT_MAX)) ||
				    (tmp_port < 0)) {
					/* Port number out of range */
					syslog(LOG_USER|LOG_DEBUG,
					    "Specified port out of range: %d",
					    tmp_port);
					return (PARSE_ADDR_PORT_OUT_OF_RANGE);
				} else {
					*port = (uint16_t)tmp_port;
				}
			} else {
				*port = defaultPort;
			}
		} else {
			*port = defaultPort;
		}

		*isIpv6 = B_TRUE;
	} else {
		/* IPv4 address */
		char *colon_pos;
		colon_pos = strchr(address_port_str, ':');
		if (!colon_pos) {
			/* No port number specified. */
			*port = defaultPort;
			(void) strlcpy(address_str, address_port_str,
			    address_str_len);
		} else {
			*colon_pos = (char)NULL;
			(void) strlcpy(address_str, address_port_str,
			    address_str_len);

			/* Extract the port number */
			colon_pos++;
			if (*colon_pos != NULL) {
				(void) strlcpy(port_str, colon_pos, 64);
				tmp_port = atoi(port_str);
				if (((tmp_port > 0) &&
				    (tmp_port > USHRT_MAX)) ||
				    (tmp_port < 0)) {
					/* Port number out of range */
					syslog(LOG_USER|LOG_DEBUG,
					    "Specified port out of range: %d",
					    tmp_port);
					return (PARSE_ADDR_PORT_OUT_OF_RANGE);
				} else {
					*port = (uint16_t)tmp_port;
				}
			} else {
				*port = defaultPort;
			}
		}

		*isIpv6 = B_FALSE;
	}

	return (PARSE_ADDR_OK);
}

/*
 * []----
 * | Following routine (number_to_scaled_string) is lifted
 * | from usr/src/cmd/fs.d/df.c
 * []----
 */
/*
 * Convert an unsigned long long to a string representation and place the
 * result in the caller-supplied buffer.
 * The given number is in units of "unit_from" size,
 * this will first be converted to a number in 1024 or 1000 byte size,
 * depending on the scaling factor.
 * Then the number is scaled down until it is small enough to be in a good
 * human readable format i.e. in the range 0 thru scale-1.
 * If it's smaller than 10 there's room enough to provide one decimal place.
 * The value "(unsigned long long)-1" is a special case and is always
 * converted to "-1".
 * Returns a pointer to the caller-supplied buffer.
 */
char *
number_to_scaled_string(
	char *buf,		/* put the result here */
	    unsigned long long number, /* convert this number */
	    int unit_from,
	    int scale)
{
	unsigned long long save = 0;
	char *M = "KMGTPE"; /* Measurement: kilo, mega, giga, tera, peta, exa */
	char *uom = M;    /* unit of measurement, initially 'K' (=M[0]) */

	if ((long long)number == (long long)-1) {
		(void) strcpy(buf, "-1");
		return (buf);
	}

	if ((number < scale) && (unit_from == 1)) {
		(void) sprintf(buf, "%4llu", number);
		return (buf);
	}
	/*
	 * Convert number from unit_from to given scale (1024 or 1000).
	 * This means multiply number by unit_from and divide by scale.
	 *
	 * Would like to multiply by unit_from and then divide by scale,
	 * but if the first multiplication would overflow, then need to
	 * divide by scale and then multiply by unit_from.
	 */
	if (number > (UINT64_MAX / (unsigned long long)unit_from)) {
		number = (number / (unsigned long long)scale) *
		    (unsigned long long)unit_from;
	} else {
		number = (number * (unsigned long long)unit_from) /
		    (unsigned long long)scale;
	}

	/*
	 * Now we have number as a count of scale units.
	 * Stop scaling when we reached exa bytes, then something is
	 * probably wrong with our number.
	 */

	while ((number >= scale) && (*uom != 'E')) {
		uom++; /* next unit of measurement */
		save = number;
		number = (number + (scale / 2)) / scale;
	}
	/* check if we should output a decimal place after the point */
	if (save && ((save / scale) < 10)) {
		/* sprintf() will round for us */
		float fnum = (float)save / scale;
		(void) sprintf(buf, "%2.1f%c", fnum, *uom);
	} else {
		(void) sprintf(buf, "%4llu%c", number, *uom);
	}
	return (buf);
}

void
stats_load_counts(tgt_node_t *n, stat_delta_t *d)
{
	tgt_node_t	*conn	= NULL,
	    *lun;
	char		*val;

	bzero(d, sizeof (*d));
	d->device = n->x_value;

	while (conn = tgt_node_next(n, XML_ELEMENT_CONN, conn)) {
		lun = NULL;
		while (lun = tgt_node_next(conn, XML_ELEMENT_LUN, lun)) {
			if (tgt_find_value_str(lun, XML_ELEMENT_READCMDS,
			    &val) == True) {
				d->read_cmds += strtoll(val, NULL, 0);
				free(val);
			}
			if (tgt_find_value_str(lun, XML_ELEMENT_WRITECMDS,
			    &val) == True) {
				d->write_cmds += strtoll(val, NULL, 0);
				free(val);
			}
			if (tgt_find_value_str(lun, XML_ELEMENT_READBLKS,
			    &val) == True) {
				d->read_blks += strtoll(val, NULL, 0);
				free(val);
			}
			if (tgt_find_value_str(lun, XML_ELEMENT_WRITEBLKS,
			    &val) == True) {
				d->write_blks += strtoll(val, NULL, 0);
				free(val);
			}
		}
	}
}

stat_delta_t *
stats_prev_counts(stat_delta_t *cp)
{
	stat_delta_t	*n;

	for (n = stat_head; n; n = n->next) {
		if (strcmp(n->device, cp->device) == 0)
			return (n);
	}
	if ((n = calloc(1, sizeof (*n))) == NULL)
		return (NULL);
	n->device = strdup(cp->device);
	if (stat_head == NULL)
		stat_head = n;
	else {
		n->next = stat_head;
		stat_head = n;
	}
	return (n);
}

void
stats_update_counts(stat_delta_t *p, stat_delta_t *c)
{
	p->read_cmds	+= c->read_cmds - p->read_cmds;
	p->write_cmds	+= c->write_cmds - p->write_cmds;
	p->read_blks	+= c->read_blks - p->read_blks;
	p->write_blks	+= c->write_blks - p->write_blks;
}

void
stats_free()
{
	stat_delta_t	*n;

	/* CSTYLED */
	for (;stat_head;) {
		n = stat_head->next;
		free(stat_head->device);
		free(stat_head);
		stat_head = n;
	}
}

static char spaces[128];

/*
 * []----
 * | dospace -- generate a string which has the appropriate number of spaces
 * |
 * | NOTE: Since this function modifies a static buffer usage of this
 * | function may not be what's expected. For example:
 * | printf("%sfoo%sbar\n", dospace(1), dospace(2)); would produce
 * | '    foo    bar'
 * | instead of
 * | '    foo        bar'
 * []----
 */
char *
dospace(int n)
{
	(void) memset(spaces, ' ', sizeof (spaces));
	spaces[sizeof (spaces) - 1] = '\0';

	if (n < sizeof (spaces))
		spaces[n * 4] = '\0';
	return (spaces);
}