/*	$NetBSD: iscsic_daemonif.c,v 1.3 2015/05/30 15:57:32 joerg Exp $	*/

/*-
 * Copyright (c) 2005,2006,2011 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Wasabi Systems, Inc.
 *
 * 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.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``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 FOUNDATION OR CONTRIBUTORS
 * 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 "iscsic_globals.h"

/*
 * do_add_target:
 *    Handle both the add_target and add_send_target commands.
 *
 *    Parameter:
 *          argc, argv  Shifted arguments
 *          kind        Which list
 *
 *    Returns:    0 if OK - else it doesn't return at all.
 */

STATIC int
do_add_target(int argc, char **argv, iscsid_list_kind_t kind)
{
	iscsid_add_target_req_t *targ;
	iscsid_set_target_authentication_req_t auth;
	iscsid_get_set_target_options_t opt;
	iscsid_response_t *rsp;
	iscsid_add_target_rsp_t *res;
	unsigned		i;
	int opts, auths, tlen;
	uint32_t tid;

	tlen = cl_get_target(&targ, argc, argv, kind != SEND_TARGETS_LIST);
	if (tlen == 0) {
		arg_missing("Target");
	}
	if (kind == SEND_TARGETS_LIST && !targ->num_portals) {
		arg_missing("Target Address");
	}
	opts = cl_get_target_opts(&opt, argc, argv);
	auths = cl_get_auth_opts(&auth, argc, argv);
	cl_get_symname(targ->sym_name, argc, argv);
	check_extra_args(argc, argv);

	targ->list_kind = kind;

	send_request(ISCSID_ADD_TARGET, tlen, targ);
	rsp = get_response(FALSE);

	if (rsp->status) {
		status_error(rsp->status);
	}
	res = (iscsid_add_target_rsp_t *)(void *)rsp->parameter;
	tid = res->target_id;

	if (kind == SEND_TARGETS_LIST) {
		printf("Added Send Target %d\n", res->target_id);
	} else {
		printf("Added Target %d", res->target_id);
		if (res->num_portals) {
			printf(", Portal ");
			for (i = 0; i < res->num_portals; i++)
				printf("%d ", res->portal_id[i]);
		}
		printf("\n");
	}

	free_response(rsp);

	if (opts) {
		opt.list_kind = kind;
		opt.target_id.id = tid;
		send_request(ISCSID_SET_TARGET_OPTIONS, sizeof(opt), &opt);
		rsp = get_response(FALSE);
		if (rsp->status) {
			status_error(rsp->status);
		}
		free_response(rsp);
	}

	if (auths) {
		auth.list_kind = kind;
		auth.target_id.id = tid;
		send_request(ISCSID_SET_TARGET_AUTHENTICATION, sizeof(auth), &auth);
		rsp = get_response(FALSE);
		if (rsp->status) {
			status_error(rsp->status);
		}
		free_response(rsp);
	}

	return 0;
}


/*
 * do_remove_target:
 *    Handle both the remove_target and remove_send_target commands.
 *
 *    Parameter:
 *          argc, argv  Shifted arguments
 *          kind        Which list
 *
 *    Returns:    0 if OK - else it doesn't return at all.
 */

STATIC int
do_remove_target(int argc, char **argv, iscsid_list_kind_t kind)
{
	iscsid_list_id_t req;
	iscsid_search_list_req_t srch;
	iscsid_response_t *rsp;

	if (!cl_get_id('I', &req.id, argc, argv)) {
		if (!cl_get_string('n', (char *)srch.strval, argc, argv)) {
			arg_missing("Target ID or Name");
		}
		check_extra_args(argc, argv);

		srch.search_kind = FIND_TARGET_NAME;
		srch.list_kind = kind;

		send_request(ISCSID_SEARCH_LIST, sizeof(srch), &srch);
		rsp = get_response(FALSE);
		if (rsp->status) {
			status_error_slist(rsp->status);
		}
		GET_SYM_ID(req.id.id, rsp->parameter);
		free_response(rsp);
	} else {
		check_extra_args(argc, argv);
	}
	req.list_kind = kind;
	send_request(ISCSID_REMOVE_TARGET, sizeof(req), &req);
	rsp = get_response(TRUE);
	if (rsp->status) {
		status_error(rsp->status);
	}
	free_response(rsp);
	printf("OK\n");
	return 0;
}


/*
 * do_refresh:
 *    Handle the refresh_targets and refresh_isns commands.
 *
 *    Parameter:
 *          argc, argv  Shifted arguments
 *          kind        Which list
 *
 *    Returns:    0 if OK - else it doesn't return at all.
 */

STATIC int
do_refresh(int argc, char **argv, iscsid_list_kind_t kind)
{
	iscsid_sym_id_t id;
	iscsid_response_t *rsp;
	iscsid_refresh_req_t req;

	req.kind = kind;

	if (cl_get_id('I', &id, argc, argv)) {
		check_extra_args(argc, argv);

		if (!id.id) {
			iscsid_search_list_req_t srch;

			srch.search_kind = FIND_NAME;
			srch.list_kind = kind;
			strlcpy((char *)srch.strval, (char *)id.name,
				sizeof(srch.strval));

			send_request(ISCSID_SEARCH_LIST, sizeof(srch), &srch);
			rsp = get_response(FALSE);
			if (rsp->status) {
				status_error_slist(rsp->status);
			}
			GET_SYM_ID(req.id[0], rsp->parameter);
			free_response(rsp);
		} else {
			req.id[0] = id.id;
		}
		req.num_ids = 1;
	} else {
		req.num_ids = 0;
		check_extra_args(argc, argv);
	}

	req.kind = kind;
	send_request(ISCSID_REFRESH_TARGETS, sizeof(req), &req);

	rsp = get_response(FALSE);
	if (rsp->status) {
		status_error(rsp->status);
	}
	printf("OK\n");
	free_response(rsp);
	return 0;
}


/*
 * add_target:
 *    Handle the add_target command.
 *
 *    Parameter:  argc, argv (shifted)
 *
 *    Returns:    0 if OK - else it doesn't return at all.
 */

int
add_target(int argc, char **argv)
{
	return do_add_target(argc, argv, TARGET_LIST);
}


/*
 * remove_target:
 *    Handle the remove_target command.
 *
 *    Parameter:  argc, argv (shifted)
 *
 *    Returns:    0 if OK - else it doesn't return at all.
 */

int
remove_target(int argc, char **argv)
{
	return do_remove_target(argc, argv, TARGET_LIST);
}


/*
 * slp_find_targets:
 *    Handle the slp_find_targets command.
 *
 *    Parameter:  argc, argv (shifted)
 *
 *    Returns:    0 if OK - else it doesn't return at all.
 *
 *    ToDo: Implement.
 */

int
/*ARGSUSED*/
slp_find_targets(int argc, char **argv)
{
	printf("Not implemented\n");
	return 5;
}


/*
 * refresh_targets:
 *    Handle the refresh_targets command.
 *
 *    Parameter:  argc, argv (shifted)
 *
 *    Returns:    0 if OK - else it doesn't return at all.
 */

int
refresh_targets(int argc, char **argv)
{
	return do_refresh(argc, argv, SEND_TARGETS_LIST);
}


/*
 * add_portal:
 *    Handle the add_portal command.
 *
 *    Parameter:
 *          argc, argv  Shifted arguments
 *
 *    Returns:    0 if OK - else it doesn't return at all.
 */

int
add_portal(int argc, char **argv)
{
	iscsid_add_portal_req_t port;
	iscsid_response_t *rsp;
	iscsid_add_portal_rsp_t *res;

	if (!cl_get_portal(&port, argc, argv)) {
		arg_missing("Portal Address");
	}
	if (!cl_get_id('I', &port.target_id, argc, argv)) {
		arg_missing("Target ID");
	}
	cl_get_symname(port.sym_name, argc, argv);
	check_extra_args(argc, argv);

	send_request(ISCSID_ADD_PORTAL, sizeof(port), &port);
	rsp = get_response(FALSE);

	if (rsp->status) {
		status_error(rsp->status);
	}
	res = (iscsid_add_portal_rsp_t *)(void *)rsp->parameter;

	printf("Added Portal %d to Target %d\n",
		res->portal_id.id, res->target_id.id);
	free_response(rsp);
	return 0;
}


/*
 * show_target:
 *    Get information about and display a target entry (including its portals).
 *
 *    Parameter:
 *       target ID
 *       list kind
 */

STATIC void
show_target(uint32_t id, iscsid_list_kind_t kind)
{
	iscsid_list_id_t req;
	iscsid_response_t *trsp, *prsp;
	iscsid_get_target_rsp_t *targ;
	iscsid_get_portal_rsp_t *port;
	unsigned i;

	/* get target info */
	req.list_kind = kind;
	req.id.id = id;
	req.id.name[0] = '\0';
	send_request(ISCSID_GET_TARGET_INFO, sizeof(req), &req);

	trsp = get_response(TRUE);
	if (trsp->status) {
		status_error(trsp->status);
	}
	targ = (iscsid_get_target_rsp_t *)(void *)trsp->parameter;

	/* display basic target info */
	printf("%6d", targ->target_id.id);
	if (targ->target_id.name[0]) {
		printf(" [%s]", targ->target_id.name);
	}
	printf(": %s", targ->TargetName);
	if (targ->TargetAlias[0]) {
		printf(" (%s)", targ->TargetAlias);
	}
	printf("\n");

	/* now get and display info on the target's portals */
	for (i = 0; i < targ->num_portals; i++) {
		req.id.id = targ->portal[i];
		send_request(ISCSID_GET_PORTAL_INFO, sizeof(req), &req);
		prsp = get_response(FALSE);
		if (prsp->status) {
			status_error(prsp->status);
		}
		port = (iscsid_get_portal_rsp_t *)(void *)prsp->parameter;

		printf("   %6d", port->portal_id.id);
		if (port->portal_id.name[0]) {
			printf(" [%s]", port->portal_id.name);
		}
		printf(": %s:%d", port->portal.address, port->portal.port);
		if (kind != SEND_TARGETS_LIST) {
			printf(",%d", port->portal.group_tag);
		}
		printf("\n");
		free_response(prsp);
	}
	free_response(trsp);
}


/*
 * do_list_targets:
 *    Handle both the list_targets and list_send_targets commands.
 *
 *    Parameter:
 *          argc, argv  Shifted arguments
 *          kind        Which list
 *
 *    Returns:    0 if OK - else it doesn't return at all.
 */

STATIC int
do_list_targets(int argc, char **argv, iscsid_list_kind_t kind)
{
	iscsid_get_list_req_t lst;
	iscsid_response_t *rsp;
	iscsid_get_list_rsp_t *list;
	unsigned i;

	check_extra_args(argc, argv);

	/* get the list of targets */
	lst.list_kind = kind;
	send_request(ISCSID_GET_LIST, sizeof(lst), &lst);
	rsp = get_response(TRUE);
	if (rsp->status) {
		status_error(rsp->status);
	}
	list = (iscsid_get_list_rsp_t *)(void *)rsp->parameter;

	/* display all targets */
	for (i = 0; i < list->num_entries; i++) {
		show_target(list->id[i], kind);
	}
	free_response(rsp);
	return 0;
}


/*
 * list_targets:
 *    Handle the list_targets command.
 *
 *    Parameter:  argc, argv (shifted)
 *
 *    Returns:    0 if OK - else it doesn't return at all.
 */

int
list_targets(int argc, char **argv)
{
	return do_list_targets(argc, argv, TARGET_LIST);
}


/*
 * add_send_target:
 *    Handle the add_target command.
 *
 *    Parameter:  argc, argv (shifted)
 *
 *    Returns:    0 if OK - else it doesn't return at all.
 */

int
add_send_target(int argc, char **argv)
{
	return do_add_target(argc, argv, SEND_TARGETS_LIST);
}


/*
 * remove_send_target:
 *    Handle the remove_target command.
 *
 *    Parameter:  argc, argv (shifted)
 *
 *    Returns:    0 if OK - else it doesn't return at all.
 */

int
remove_send_target(int argc, char **argv)
{
	return do_remove_target(argc, argv, SEND_TARGETS_LIST);
}


/*
 * list_send_targets:
 *    Handle the list_send_targets command.
 *
 *    Parameter:  argc, argv (shifted)
 *
 *    Returns:    0 if OK - else it doesn't return at all.
 */

int
list_send_targets(int argc, char **argv)
{
	return do_list_targets(argc, argv, SEND_TARGETS_LIST);
}


/*
 * add_isns_server:
 *    Handle the add_isns_server command.
 *
 *    Parameter:  argc, argv (shifted)
 *
 *    Returns:    0 if OK - else it doesn't return at all.
 */

int
add_isns_server(int argc, char **argv)
{
	iscsid_add_isns_server_req_t arg;
	iscsid_add_isns_server_rsp_t *res;
	iscsid_response_t *rsp;

	memset(&arg, 0x0, sizeof(arg));
	if (!(cl_get_isns(&arg, argc, argv))) {
		arg_missing("Server Address");
	}
	check_extra_args(argc, argv);

	send_request(ISCSID_ADD_ISNS_SERVER, sizeof(arg), &arg);
	rsp = get_response(FALSE);

	if (rsp->status) {
		status_error(rsp->status);
	}
	res = (iscsid_add_isns_server_rsp_t *)(void *)rsp->parameter;

	printf("Added iSNS Server ID %d\n", res->server_id);

	free_response(rsp);
	return 0;
}


/*
 * remove_isns_server:
 *    Handle the add_isns_server command.
 *
 *    Parameter:  argc, argv (shifted)
 *
 *    Returns:    0 if OK - else it doesn't return at all.
 */

int
remove_isns_server(int argc, char **argv)
{
	iscsid_sym_id_t req;
	iscsid_search_list_req_t srch;
	iscsid_response_t *rsp;

	if (!cl_get_id('I', &req, argc, argv)) {
		if (!cl_get_string('a', (char *)srch.strval, argc, argv)) {
			arg_missing("Server Address");
		}
		check_extra_args(argc, argv);
		srch.search_kind = FIND_ADDRESS;
		srch.list_kind = ISNS_LIST;

		send_request(ISCSID_SEARCH_LIST, sizeof(srch), &srch);
		rsp = get_response(FALSE);
		if (rsp->status) {
			status_error_slist(rsp->status);
		}
		GET_SYM_ID(req.id, rsp->parameter);
		free_response(rsp);
	} else {
		check_extra_args(argc, argv);
	}
	send_request(ISCSID_REMOVE_ISNS_SERVER, sizeof(req), &req);
	rsp = get_response(TRUE);
	if (rsp->status) {
		status_error(rsp->status);
	}
	free_response(rsp);

	printf("OK\n");
	return 0;
}


/*
 * find_isns_servers:
 *    Handle the find_isns_servers command.
 *
 *    Parameter:  argc, argv (shifted)
 *
 *    Returns:    0 if OK - else it doesn't return at all.
 */

int
/*ARGSUSED*/
find_isns_servers(int argc, char **argv)
{
	printf("Not implemented\n");
	return 5;
}


/*
 * list_isns_servers:
 *    Handle the list_isns_servers command.
 *
 *    Parameter:  argc, argv (shifted)
 *
 *    Returns:    0 if OK - else it doesn't return at all.
 */

int
list_isns_servers(int argc, char **argv)
{
	iscsid_get_list_req_t lst;
	iscsid_response_t *rsp, *prsp;
	iscsid_get_list_rsp_t *list;
	iscsid_get_isns_server_rsp_t *isns;
	iscsid_sym_id_t req;
	unsigned i;

	memset(&req, 0x0, sizeof(req));
	check_extra_args(argc, argv);

	/* get the list of servers */
	lst.list_kind = ISNS_LIST;
	send_request(ISCSID_GET_LIST, sizeof(lst), &lst);
	rsp = get_response(TRUE);
	if (rsp->status) {
		status_error(rsp->status);
	}
	list = (iscsid_get_list_rsp_t *)(void *)rsp->parameter;

	/* display all servers */
	for (i = 0; i < list->num_entries; i++) {
		req.id = list->id[i];
		send_request(ISCSID_GET_ISNS_SERVER, sizeof(req), &req);
		prsp = get_response(FALSE);
		if (prsp->status)
			status_error(prsp->status);

		isns = (iscsid_get_isns_server_rsp_t *)(void *)prsp->parameter;
		printf("%6d: %s\n", list->id[i], isns->address);
		free_response(prsp);
	}
	free_response(rsp);
	return 0;
}


/*
 * refresh_isns:
 *    Handle the refresh_isns command.
 *
 *    Parameter:  argc, argv (shifted)
 *
 *    Returns:    0 if OK - else it doesn't return at all.
 */

int
refresh_isns(int argc, char **argv)
{
	return do_refresh(argc, argv, ISNS_LIST);
}


/*
 * add_initiator:
 *    Handle the add_initiator command.
 *
 *    Parameter:  argc, argv (shifted)
 *
 *    Returns:    0 if OK - else it doesn't return at all.
 */

int
add_initiator(int argc, char **argv)
{
	iscsid_add_initiator_req_t req;
	iscsid_add_initiator_rsp_t *res;
	iscsid_response_t *rsp;

	memset(&req, 0x0, sizeof(req));
	if (!cl_get_string('a', (char *)req.address, argc, argv)) {
		arg_missing("Interface Address");
	}
	cl_get_symname(req.name, argc, argv);
	check_extra_args(argc, argv);

	send_request(ISCSID_ADD_INITIATOR_PORTAL, sizeof(req), &req);
	rsp = get_response(FALSE);
	if (rsp->status) {
		status_error(rsp->status);
	}
	res = (iscsid_add_initiator_rsp_t *)(void *)rsp->parameter;
	printf("Added Initiator Portal %d\n", res->portal_id);

	free_response(rsp);
	return 0;
}


/*
 * remove_initiator:
 *    Handle the remove_initiator command.
 *
 *    Parameter:  argc, argv (shifted)
 *
 *    Returns:    0 if OK - else it doesn't return at all.
 */

int
remove_initiator(int argc, char **argv)
{
	iscsid_sym_id_t req;
	iscsid_response_t *rsp;

	memset(&req, 0x0, sizeof(req));
	if (!cl_get_id('I', &req, argc, argv)) {
		arg_missing("Initiator Portal ID");
	}
	check_extra_args(argc, argv);

	send_request(ISCSID_REMOVE_INITIATOR_PORTAL, sizeof(req), &req);
	rsp = get_response(FALSE);
	if (rsp->status) {
		status_error(rsp->status);
	}
	free_response(rsp);

	printf("OK\n");
	return 0;
}

/*
 * list_initiators:
 *    Handle the list_initiators command.
 *
 *    Parameter:  argc, argv (shifted)
 *
 *    Returns:    0 if OK - else it doesn't return at all.
 */

int
list_initiators(int argc, char **argv)
{
	iscsid_get_list_req_t lst;
	iscsid_response_t *rsp, *prsp;
	iscsid_get_list_rsp_t *list;
	iscsid_get_initiator_rsp_t *init;
	iscsid_sym_id_t req;
	unsigned i;

	memset(&req, 0x0, sizeof(req));
	check_extra_args(argc, argv);

	/* get the list of servers */
	lst.list_kind = INITIATOR_LIST;
	send_request(ISCSID_GET_LIST, sizeof(lst), &lst);
	rsp = get_response(TRUE);
	if (rsp->status) {
		status_error(rsp->status);
	}
	list = (iscsid_get_list_rsp_t *)(void *)rsp->parameter;

	/* display all servers */
	for (i = 0; i < list->num_entries; i++) {
		req.id = list->id[i];
		send_request(ISCSID_GET_INITIATOR_PORTAL, sizeof(req), &req);
		prsp = get_response(FALSE);
		if (prsp->status) {
			status_error(prsp->status);
		}
		init = (iscsid_get_initiator_rsp_t *)(void *)prsp->parameter;
		printf("%6d", init->portal_id.id);
		if (init->portal_id.name[0]) {
			printf("[%s]", init->portal_id.name);
		}
		printf(": %s\n", init->address);
		free_response(prsp);
	}
	free_response(rsp);
	return 0;
}


/*
 * login_or_add:
 *    Common routine for login and add_connection. Handles the actual command,
 *    the only pre-initialized component is the session ID in the login request
 *    for add_connection.
 *
 *    Parameters: the login request, plus argc & argv
*/

STATIC void
login_or_add(iscsid_login_req_t * loginp, int argc, char **argv)
{
	iscsid_add_target_req_t *targ = NULL;
	iscsid_add_target_rsp_t	*tgtrsp;
	iscsid_set_target_authentication_req_t auth;
	iscsid_get_set_target_options_t opt;
	iscsid_search_list_req_t srch;
	iscsid_response_t *rsp;
	int opts, auths, tlen;

	/* get the remaining parameters */
	opts = cl_get_target_opts(&opt, argc, argv);
	auths = cl_get_auth_opts(&auth, argc, argv);
	cl_get_symname(loginp->sym_name, argc, argv);
	cl_get_id ('i', &loginp->initiator_id, argc, argv);

	/* do we have a portal ID? */
	if (!cl_get_id('P', &loginp->portal_id, argc, argv)) {
		/* No portal ID - then we must have a target name */
		if (!(tlen = cl_get_target(&targ, argc, argv, TRUE))) {
			if (NO_ID(&loginp->session_id)) {
				arg_missing("Target");
			}
		}

		check_extra_args(argc, argv);

		if (targ != NULL) {
			/* was a complete target with portal specified? */
			if (targ->num_portals) {
				/* then add it like in an add_target command */
				targ->list_kind = TARGET_LIST;
				send_request(ISCSID_ADD_TARGET, tlen, targ);
				rsp = get_response(FALSE);
				if (rsp->status) {
					status_error(rsp->status);
				}
				tgtrsp = (iscsid_add_target_rsp_t *)(void *)(rsp->parameter);
				(void) memcpy(&loginp->portal_id.id, &tgtrsp->target_id,
					sizeof(loginp->portal_id.id));
			} else {
				/* else find the target by its TargetName */
				srch.search_kind = FIND_TARGET_NAME;
				srch.list_kind = TARGET_LIST;
				strlcpy((char *)srch.strval, (char *)targ->TargetName,
					sizeof(srch.strval));

				send_request(ISCSID_SEARCH_LIST, sizeof(srch), &srch);
				rsp = get_response(FALSE);
				if (rsp->status) {
					status_error_slist(rsp->status);
				}
				GET_SYM_ID(loginp->portal_id.id, rsp->parameter);
			}
			free_response(rsp);
		}
	} else
		check_extra_args(argc, argv);

	/* Options specified? Then set them */
	if (opts) {
		opt.target_id = loginp->portal_id;
		opt.list_kind = TARGET_LIST;
		send_request(ISCSID_SET_TARGET_OPTIONS, sizeof(opt), &opt);
		rsp = get_response(FALSE);
		if (rsp->status) {
			status_error(rsp->status);
		}
		free_response(rsp);
	}
	/* Authentication options specified? Then set them */
	if (auths) {
		auth.target_id = loginp->portal_id;
		auth.list_kind = TARGET_LIST;
		send_request(ISCSID_SET_TARGET_AUTHENTICATION, sizeof(auth), &auth);
		rsp = get_response(FALSE);
		if (rsp->status) {
			status_error(rsp->status);
		}
		free_response(rsp);
	}
}


/*
 * login:
 *    Handle login command.
 *
 *    Parameter:  argc, argv (shifted)
 *
 *    Returns:    0 if OK - else it doesn't return at all.
*/

int
login(int argc, char **argv)
{
	iscsid_login_req_t loginp;
	iscsid_login_rsp_t *res;
	iscsid_response_t *rsp;

	memset(&loginp, 0x0, sizeof(loginp));
	loginp.login_type = (cl_get_opt('m', argc, argv))
						? ISCSI_LOGINTYPE_NOMAP : ISCSI_LOGINTYPE_MAP;

	login_or_add(&loginp, argc, argv);

	send_request(ISCSID_LOGIN, sizeof(loginp), &loginp);

	rsp = get_response(FALSE);
	if (rsp->status) {
		status_error(rsp->status);
	}
	res = (iscsid_login_rsp_t *)(void *)rsp->parameter;
	printf("Created Session %d, Connection %d\n",
		res->session_id.id, res->connection_id.id);

	free_response(rsp);
	return 0;
}


/*
 * logout:
 *    Handle logout command.
 *
 *    Parameter:  argc, argv (shifted)
 *
 *    Returns:    0 if OK - else it doesn't return at all.
*/

int
logout(int argc, char **argv)
{
	iscsid_sym_id_t req;
	iscsid_response_t *rsp;

	if (!cl_get_id('I', &req, argc, argv)) {
		arg_missing("Session");
	}
	check_extra_args(argc, argv);

	send_request(ISCSID_LOGOUT, sizeof(req), &req);
	rsp = get_response(FALSE);
	if (rsp->status) {
		status_error(rsp->status);
	}
	free_response(rsp);

	printf("OK\n");
	return 0;
}


/*
 * add_connection:
 *    Handle add_connection command.
 *
 *    Parameter:  argc, argv (shifted)
 *
 *    Returns:    0 if OK - else it doesn't return at all.
*/

int
add_connection(int argc, char **argv)
{
	iscsid_login_req_t loginp;
	iscsid_login_rsp_t	*loginrsp;
	iscsid_response_t *rsp;

	memset(&loginp, 0x0, sizeof(loginp));
	loginp.login_type = ISCSI_LOGINTYPE_MAP;
	cl_get_id('I', &loginp.session_id, argc, argv);

	login_or_add(&loginp, argc, argv);

	send_request(ISCSID_ADD_CONNECTION, sizeof(loginp), &loginp);

	rsp = get_response(FALSE);
	if (rsp->status) {
		status_error(rsp->status);
	}
	loginrsp = (iscsid_login_rsp_t *)(void *)(rsp->parameter);
	printf("Added Connection %d\n", loginrsp->connection_id.id);

	free_response(rsp);
	return 0;
}


/*
 * remove_connection:
 *    Handle remove connection command
 *
 *    Parameter:  argc, argv (shifted)
 *
 *    Returns:    0 if OK - else it doesn't return at all.
*/

int
remove_connection(int argc, char **argv)
{
	iscsid_remove_connection_req_t req;
	iscsid_response_t *rsp;

	if (!cl_get_id('I', &req.session_id, argc, argv)) {
		arg_missing("Session");
	}
	if (!cl_get_id('C', &req.connection_id, argc, argv)) {
		arg_missing("Connection");
	}
	check_extra_args(argc, argv);

	send_request(ISCSID_REMOVE_CONNECTION, sizeof(req), &req);
	rsp = get_response(FALSE);
	if (rsp->status) {
		status_error(rsp->status);
	}
	free_response(rsp);

	printf("OK\n");
	return 0;
}


/*
 * list_sessions:
 *    Handle list_sessions command.
 *
 *    Parameter:  argc, argv (shifted)
 *
 *    Returns:    0 if OK - else it doesn't return at all.
 *
 * ToDo: initiator portals, connection recurse
*/

int
list_sessions(int argc, char **argv)
{
	iscsid_response_t *lrsp, *rsp;
	iscsid_get_session_list_rsp_t *list;
	iscsid_session_list_entry_t *ent;
	iscsid_get_connection_info_req_t creq;
	iscsid_get_connection_info_rsp_t *info;
	iscsid_sym_id_t clreq;
	iscsid_get_connection_list_rsp_t *clinfo;
	iscsid_connection_list_entry_t *clent;
	unsigned i;
	unsigned j;
	int lconn;

	memset(&creq, 0x0, sizeof(creq));
	memset(&clreq, 0x0, sizeof(clreq));
	lconn = cl_get_opt('c', argc, argv);

	check_extra_args(argc, argv);

	send_request(ISCSID_GET_SESSION_LIST, 0, NULL);
	lrsp = get_response(TRUE);
	if (lrsp->status) {
		status_error(lrsp->status);
	}
	list = (iscsid_get_session_list_rsp_t *)(void *)lrsp->parameter;

	for (i = 0, ent = list->session; i < list->num_entries; i++, ent++) {
		creq.session_id.id = ent->session_id.id;
		send_request(ISCSID_GET_CONNECTION_INFO, sizeof(creq), &creq);
		rsp = get_response(FALSE);
		if (rsp->status) {
			status_error(rsp->status);
		}
		info = (iscsid_get_connection_info_rsp_t *)(void *)rsp->parameter;

		printf("Session %d", info->session_id.id);
		if (info->session_id.name[0]) {
			printf("[%s]", info->session_id.name);
		}
		printf(": Target %s", info->TargetName);
		if (info->target_portal_id.name[0]) {
			printf(" [%s]", info->target_portal_id.name);
		}
		printf("\n");

		if (lconn) {
			clreq.id = info->session_id.id;
			send_request(ISCSID_GET_CONNECTION_LIST, sizeof(clreq), &clreq);
			rsp = get_response(FALSE);
			if (rsp->status) {
				status_error(rsp->status);
			}
			clinfo = (iscsid_get_connection_list_rsp_t *)(void *)rsp->parameter;

			for (j = 0, clent = clinfo->connection;
				 j < clinfo->num_connections; j++, clent++) {
				printf(" Connection %d", clent->connection_id.id);
				if (clent->connection_id.name[0]) {
					printf("[%s]", clent->connection_id.name);
				}
				printf(" Address %s:%d,%d\n", clent->target_portal.address,
					clent->target_portal.port,
					clent->target_portal.group_tag);
			}
		}
	}

	return 0;
}


/*
 * set_node_name:
 *    Handle the set_node_name command.
 *
 *    Parameter:  argc, argv (shifted)
 *
 *    Returns:    0 if OK - else it doesn't return at all.
 */

int
set_node_name(int argc, char **argv)
{
	iscsid_set_node_name_req_t req;
	iscsid_response_t *rsp;
	uint64_t isid;

	memset(&req, 0x0, sizeof(req));
	if (!cl_get_string('n', (char *)req.InitiatorName, argc, argv)) {
		arg_missing("Initiator Name");
	}
	cl_get_string('A', (char *)req.InitiatorAlias, argc, argv);
	isid = cl_get_longlong('i', argc, argv);
	hton6(req.ISID, isid);

	check_extra_args(argc, argv);

	send_request(ISCSID_SET_NODE_NAME, sizeof(req), &req);

	rsp = get_response(FALSE);
	if (rsp->status) {
		status_error(rsp->status);
	}
	free_response(rsp);

	printf("OK\n");
	return 0;
}

/*
 * get_version:
 *    Handle the version command.
 *
 *    Parameter:  argc, argv (shifted)
 *
 *    Returns:    0 if OK - else it doesn't return at all.
 */

int
/*ARGSUSED*/
get_version(int argc, char **argv)
{
	iscsid_response_t *rsp;
	iscsid_get_version_rsp_t *ver;

	send_request(ISCSID_GET_VERSION, 0, NULL);
	rsp = get_response(FALSE);
	if (rsp->status) {
		status_error(rsp->status);
	}
	ver = (iscsid_get_version_rsp_t *)(void *)rsp->parameter;
	printf("%s\n%s\n%s\n", VERSION_STRING, ver->version_string,
		   ver->driver_version_string);

	return 0;
}
