/* $NetBSD: mount_iscsi.c,v 1.3 2006/03/31 23:22:24 agc Exp $ */

/*
 * Copyright © 2005 Alistair Crooks.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote
 *    products derived from this software without specific prior written
 *    permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#include <sys/cdefs.h>

#ifndef lint
__COPYRIGHT("@(#) Copyright © 2005 \
	        The NetBSD Foundation, Inc.  All rights reserved.");
__RCSID("$NetBSD: mount_iscsi.c,v 1.3 2006/03/31 23:22:24 agc Exp $");
#endif

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

#include <netinet/in.h>

#include <arpa/inet.h>

#include <netdb.h>
#include <regex.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>

#include "iscsi.h"
#include "initiator.h"
#include "tests.h"

static int	verbose;

enum {
	ISCSI_DOMAIN_NAME_SIZE = 512,
	ISCSI_PRODUCT_NAME_SIZE = 256,
	ISCSI_TARGET_NAME_SIZE = 256,
	ISCSI_LUN_NAME_SIZE = 64,

	MAX_DOMAIN_PARTS = 100
};

/* iSCSI addresses are specified using an IQN, or iSCSI Qualified Name */
/* this structure is used to unpack the components of an IQN */
typedef struct iqn_t {
	char			revdomain[ISCSI_DOMAIN_NAME_SIZE];
	char			domain[ISCSI_DOMAIN_NAME_SIZE];
	char			product[ISCSI_PRODUCT_NAME_SIZE];
	char			target[ISCSI_TARGET_NAME_SIZE];
	char			lun[ISCSI_LUN_NAME_SIZE];
	struct sockaddr_in	sock;
	int			socklen;
} iqn_t;

/* regular expression used to parse IQNs */
#define IQN_PARSE	"iqn\\.[^.]*\\.(.*)\\.([^.]*)\\.([^.]*)\\.([^.]*)"

/* copy a name to an output array */
static void
copyname(char *array, size_t size, char *s, int from, int to, const char *field)
{
	if (to - from > size - 1) {
		warnx("truncating %s `%.*s'",  field, (int)(to - from), &s[from]);
	}
	(void) strlcpy(array, &s[from], MIN((int)(to - from + 1), size));
}

/* reverse the components of the domain name */
static void
reversedomain(char *in, char *out, size_t size)
{
	char	*cp;
	char	*dots[MAX_DOMAIN_PARTS];
	int	 dotc;
	int	 i;

	for (cp = in, dotc = 0 ; *cp != 0x0 && dotc < MAX_DOMAIN_PARTS ; cp++) {
		if (*cp == '.') {
			dots[dotc++] = cp;
		}
	}
	(void) memset(out, 0x0, size);
	for (cp = out, i = dotc - 1 ; i >= 0 ; --i) {
		cp += snprintf(cp, size - (int)(cp - out), "%s%.*s",
			(i == dotc - 1) ? "" : ".",
			(i == dotc - 1) ? strlen(dots[i]) - 1 : (int)(dots[i + 1] - dots[i] - 1),
			dots[i] + 1);
	}
	(void) snprintf(cp, size - (int)(cp - out), "%s%.*s", ".", (int)(dots[0] - in), in);
}

/* iqn.2003-02.com.alistaircrooks.product.target.lun */
static int
iqnparse(char *s, iqn_t *iqn)
{
	static int	done;
	regmatch_t	matchv[10];
	regex_t		r;

	(void) memset(iqn, 0x0, sizeof(*iqn));
	if (!done) {
		(void) memset(&r, 0x0, sizeof(r));
		if (regcomp(&r, IQN_PARSE, REG_EXTENDED) != 0) {
			warn("failed to compile iqn parse string");
		}
		done = 1;
	}
	if (regexec(&r, s, 10, matchv, 0) != 0) {
		warnx("iqnparse: invalid IQN - `%s'", s);
		return 0;
	}

	/* get the domain name from the iqn and reverse it */
	copyname(iqn->revdomain, sizeof(iqn->revdomain), s, (int)(matchv[1].rm_so), (int)(matchv[1].rm_eo), "revdomain");
	reversedomain(iqn->revdomain, iqn->domain, sizeof(iqn->domain));

	/* get the product name from the iqn */
	copyname(iqn->product, sizeof(iqn->product), s, (int)(matchv[2].rm_so), (int)(matchv[2].rm_eo), "product");

	/* get the target name from the iqn */
	copyname(iqn->target, sizeof(iqn->target), s, (int)(matchv[3].rm_so), (int)(matchv[3].rm_eo), "target");

	/* get the lun from the iqn */
	copyname(iqn->lun, sizeof(iqn->lun), s, (int)(matchv[4].rm_so), (int)(matchv[4].rm_eo), "lun");

	return 1;
}

/* make a connection to an IQN */
static int
make_connection(char *hostname, iqn_t *iqn)
{
	struct addrinfo		 hints;
	struct addrinfo		*res;
	struct addrinfo		*res0 = NULL;
	volatile int		 s;
	in_port_t		 portnum;
	char			 hbuf[NI_MAXHOST];
	char			 service[32];
	char			*host;
	int			 error;

	(void) memset(iqn, 0x0, sizeof(*iqn));
	(void) iqnparse(hostname, iqn);
	printf("revdomain: `%s'\n", iqn->revdomain);
	printf("domain: `%s'\n", iqn->domain);
	printf("product: `%s'\n", iqn->product);
	printf("target: `%s'\n", iqn->target);
	printf("lun: `%s'\n", iqn->lun);

	(void) memset(&hints, 0x0, sizeof(hints));
	(void) strlcpy(service, "iscsi", sizeof(service));
	hints.ai_flags = 0;
	hints.ai_family = AF_INET;	/* expand this to IPv6 at the right time - XXX */
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_protocol = 0;
	if ((error = getaddrinfo(host = iqn->domain, NULL, &hints, &res0)) != 0) {
		warnx("%s: %s", host, gai_strerror(error));
		return 0;
	}
	if (res0->ai_canonname) {
		host = res0->ai_canonname;
	}

	s = -1;
	for (res = res0; res; res = res->ai_next) {
#if 0
		/* XXX fixme */
		/*
		 * see comment in hookup()
		 */
		ai_unmapped(res);
#endif
		if (getnameinfo(res->ai_addr, res->ai_addrlen, hbuf, sizeof(hbuf), service, sizeof(service), NI_NUMERICHOST) != 0) {
			strlcpy(hbuf, "invalid", sizeof(hbuf));
		}

		((struct sockaddr_in *)res->ai_addr)->sin_port = htons(portnum);
		(void) memcpy(&iqn->sock, &res->ai_addr, sizeof(res->ai_addr));
		iqn->socklen = res->ai_addrlen;
		return 1;

#if 0
		if ((s = socket(res->ai_family, SOCK_STREAM, res->ai_protocol)) < 0) {
			warn("Can't create socket");
			continue;
		}

		if (connect(s, res->ai_addr, res->ai_addrlen) < 0) {
			warn("Connect to address `%s'", hbuf);
			close(s);
			s = -1;
			continue;
		}
#endif
	}

	return 0;
}

int
main(int argc, char **argv)
{
	struct sigaction	act;
	iqn_t			iqn;
	int             	begin_tid = 0;
	int             	end_tid = CONFIG_INITIATOR_NUM_TARGETS;
	int             	lun = 0;
	int			i;
	int			j;

	while ((i = getopt(argc, argv, "v")) != -1) {
		switch(i) {
		case 'v':
			verbose += 1;
			break;
		default:
			errx(EXIT_FAILURE, "Unknown option `%c'", i);
			break;
		}
	}
	if (argc - optind != 2) {
		errx(EXIT_FAILURE, "Usage: %s [-v] iqn mountpoint\n", *argv);
	}
	(void) memset(&iqn, 0x0, sizeof(iqn));
	if (!make_connection(argv[optind], &iqn)) {
		exit(EXIT_FAILURE);
	}
	for (j = 0; j < 1; j++) {

		printf("<ITER %i>\n", j);

		/* Ignore sigpipe */

		act.sa_handler = SIG_IGN;
		sigaction(SIGPIPE, &act, NULL);

		/* Initialize Initiator */

		if (initiator_init(iqn.domain) == -1) {
			iscsi_trace_error(__FILE__, __LINE__, "initiator_init() failed\n");
			return -1;
		}
		/* Run tests for each target */

		for (i = begin_tid; i < end_tid; i++) {
			if (test_all(i, lun) != 0) {
				iscsi_trace_error(__FILE__, __LINE__, "test_all() failed\n");
				return -1;
			}
		}

		/* Shutdown Initiator */

		if (initiator_shutdown() == -1) {
			iscsi_trace_error(__FILE__, __LINE__, "initiator_shutdown() failed\n");
			return -1;
		}
	}
	exit(EXIT_SUCCESS);
}


syntax highlighted by Code2HTML, v. 0.9.1