xref: /netbsd-src/sbin/iscsictl/iscsic_main.c (revision 44f0e14601b1088fbb036e32df0ddc4b341209c0)
1*44f0e146Sjoerg /*	$NetBSD: iscsic_main.c,v 1.6 2015/05/30 15:57:32 joerg Exp $	*/
275a17f3cSagc 
375a17f3cSagc /*-
475a17f3cSagc  * Copyright (c) 2005,2006,2011 The NetBSD Foundation, Inc.
575a17f3cSagc  * All rights reserved.
675a17f3cSagc  *
775a17f3cSagc  * This code is derived from software contributed to The NetBSD Foundation
875a17f3cSagc  * by Wasabi Systems, Inc.
975a17f3cSagc  *
1075a17f3cSagc  * Redistribution and use in source and binary forms, with or without
1175a17f3cSagc  * modification, are permitted provided that the following conditions
1275a17f3cSagc  * are met:
1375a17f3cSagc  * 1. Redistributions of source code must retain the above copyright
1475a17f3cSagc  *    notice, this list of conditions and the following disclaimer.
1575a17f3cSagc  * 2. Redistributions in binary form must reproduce the above copyright
1675a17f3cSagc  *    notice, this list of conditions and the following disclaimer in the
1775a17f3cSagc  *    documentation and/or other materials provided with the distribution.
1875a17f3cSagc  *
1975a17f3cSagc  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
2075a17f3cSagc  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2175a17f3cSagc  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2275a17f3cSagc  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2375a17f3cSagc  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2475a17f3cSagc  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2575a17f3cSagc  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2675a17f3cSagc  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2775a17f3cSagc  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2875a17f3cSagc  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2975a17f3cSagc  * POSSIBILITY OF SUCH DAMAGE.
3075a17f3cSagc  */
3175a17f3cSagc #include <sys/types.h>
3275a17f3cSagc #include <sys/param.h>
3375a17f3cSagc #include <sys/socket.h>
3475a17f3cSagc #include <sys/un.h>
3575a17f3cSagc 
3675a17f3cSagc #include "iscsic_globals.h"
3775a17f3cSagc 
3875a17f3cSagc #include <err.h>
3975a17f3cSagc #include <errno.h>
4075a17f3cSagc #include <fcntl.h>
4175a17f3cSagc #include <stdarg.h>
4275a17f3cSagc 
4375a17f3cSagc #define DEVICE    "/dev/iscsi0"
4475a17f3cSagc 
4575a17f3cSagc #define ISCSICTL_VERSION	"20110805"
4675a17f3cSagc 
4775a17f3cSagc /*
4875a17f3cSagc  *-------- Table of commands and the associated handler function -------------
4975a17f3cSagc */
5075a17f3cSagc 
5175a17f3cSagc static command_t cmds[] = {
5275a17f3cSagc 	{"version", get_version},
5375a17f3cSagc 	{"add_target", add_target},
5475a17f3cSagc 	{"add_portal", add_portal},
5575a17f3cSagc 	{"remove_target", remove_target},
5675a17f3cSagc 	{"slp_find_targets", slp_find_targets},
5775a17f3cSagc 	{"refresh_targets", refresh_targets},
5875a17f3cSagc 	{"list_targets", list_targets},
5975a17f3cSagc 	{"add_send_target", add_send_target},
6075a17f3cSagc 	{"remove_send_target", remove_send_target},
6175a17f3cSagc 	{"list_send_targets", list_send_targets},
6275a17f3cSagc 	{"add_isns_server", add_isns_server},
6375a17f3cSagc 	{"remove_isns_server", remove_isns_server},
6475a17f3cSagc 	{"find_isns_servers", find_isns_servers},
6575a17f3cSagc 	{"list_isns_servers", list_isns_servers},
6675a17f3cSagc 	{"refresh_isns", refresh_isns},
6775a17f3cSagc 	{"login", login},
6875a17f3cSagc 	{"logout", logout},
6975a17f3cSagc 	{"add_connection", add_connection},
7075a17f3cSagc 	{"remove_connection", remove_connection},
7175a17f3cSagc 	{"inquiry", inquiry},
7275a17f3cSagc 	{"read_capacity", read_capacity},
7375a17f3cSagc 	{"report_luns", report_luns},
7475a17f3cSagc 	{"test_unit_ready", test_unit_ready},
7575a17f3cSagc 	{"add_initiator", add_initiator},
7675a17f3cSagc 	{"remove_initiator", remove_initiator},
7775a17f3cSagc 	{"list_initiators", list_initiators},
7875a17f3cSagc 	{"list_sessions", list_sessions},
7975a17f3cSagc 	{"set_node_name", set_node_name},
8075a17f3cSagc 	{NULL, NULL}
8175a17f3cSagc };
8275a17f3cSagc 
8375a17f3cSagc 
8475a17f3cSagc /*
8575a17f3cSagc  *-------- Table of error codes and the associated message text -------------
8675a17f3cSagc */
8775a17f3cSagc 
8875a17f3cSagc typedef struct {
8975a17f3cSagc 	unsigned	 code;
9075a17f3cSagc 	const char	*str;
9175a17f3cSagc } status_msg_t;
9275a17f3cSagc 
9375a17f3cSagc static status_msg_t status_msg[] = {
9475a17f3cSagc 	{ISCSI_STATUS_LIST_EMPTY, "The list is empty"},
9575a17f3cSagc 	{ISCSI_STATUS_DUPLICATE_NAME, "The specified name is not unique"},
9675a17f3cSagc 	{ISCSI_STATUS_GENERAL_ERROR, "A non-specific error occurred"},
9775a17f3cSagc 	{ISCSI_STATUS_LOGIN_FAILED, "The login failed"},
9875a17f3cSagc 	{ISCSI_STATUS_CONNECTION_FAILED, "The attempt to establish a connection failed"},
9975a17f3cSagc 	{ISCSI_STATUS_AUTHENTICATION_FAILED, "Authentication negotiation failed"},
10075a17f3cSagc 	{ISCSI_STATUS_NO_RESOURCES, "Could not allocate resources (e.g. memory)"},
10175a17f3cSagc 	{ISCSI_STATUS_MAXED_CONNECTIONS, "Maximum number of connections exceeded"},
10275a17f3cSagc 	{ISCSI_STATUS_INVALID_SESSION_ID, "Session ID not found"},
10375a17f3cSagc 	{ISCSI_STATUS_INVALID_CONNECTION_ID, "Connection ID not found"},
10475a17f3cSagc 	{ISCSI_STATUS_INVALID_SOCKET, "Specified socket is invalid"},
10575a17f3cSagc 	{ISCSI_STATUS_NOTIMPL, "Feature not implemented"},
10675a17f3cSagc 	{ISCSI_STATUS_CHECK_CONDITION, "Target reported CHECK CONDITION"},
10775a17f3cSagc 	{ISCSI_STATUS_TARGET_BUSY, "Target reported BUSY"},
10875a17f3cSagc 	{ISCSI_STATUS_TARGET_ERROR, "Target reported other error"},
10975a17f3cSagc 	{ISCSI_STATUS_TARGET_FAILURE, "Command Response was Target Failure"},
11075a17f3cSagc 	{ISCSI_STATUS_TARGET_DROP, "Target dropped connection"},
11175a17f3cSagc 	{ISCSI_STATUS_SOCKET_ERROR, "Communication failure"},
11275a17f3cSagc 	{ISCSI_STATUS_PARAMETER_MISSING, "A required ioctl parameter is missing"},
11375a17f3cSagc 	{ISCSI_STATUS_PARAMETER_INVALID, "A parameter is malformed (string too long etc.)"},
11475a17f3cSagc 	{ISCSI_STATUS_MAP_FAILED, "Mapping the LUNs failed"},
11575a17f3cSagc 	{ISCSI_STATUS_NO_INITIATOR_NAME, "Initiator name not set"},
11675a17f3cSagc 	{ISCSI_STATUS_NEGOTIATION_ERROR, "Negotiation failure (invalid key or value)"},
11775a17f3cSagc 	{ISCSI_STATUS_TIMEOUT, "Command timed out (at iSCSI level)"},
11875a17f3cSagc 	{ISCSI_STATUS_PROTOCOL_ERROR, "Internal Error (Protocol error reject)"},
11975a17f3cSagc 	{ISCSI_STATUS_PDU_ERROR, "Internal Error (Invalid PDU field reject)"},
12075a17f3cSagc 	{ISCSI_STATUS_CMD_NOT_SUPPORTED, "Target does not support iSCSI command"},
12175a17f3cSagc 	{ISCSI_STATUS_DRIVER_UNLOAD, "Driver is unloading"},
12275a17f3cSagc 	{ISCSI_STATUS_LOGOUT, "Session was logged out"},
12375a17f3cSagc 	{ISCSI_STATUS_PDUS_LOST, "Excessive PDU loss"},
12475a17f3cSagc 	{ISCSI_STATUS_INVALID_EVENT_ID, "Invalid Event ID"},
12575a17f3cSagc 	{ISCSI_STATUS_EVENT_DEREGISTERED, "Wait for event cancelled by deregistration"},
12675a17f3cSagc 	{ISCSI_STATUS_EVENT_WAITING, "Already waiting for event"},
12775a17f3cSagc 	{ISCSI_STATUS_TASK_NOT_FOUND, "Task Management: task not found"},
12875a17f3cSagc 	{ISCSI_STATUS_LUN_NOT_FOUND, "Task Management: LUN not found"},
12975a17f3cSagc 	{ISCSI_STATUS_TASK_ALLEGIANT, "Task Management: Task still allegiant"},
13075a17f3cSagc 	{ISCSI_STATUS_CANT_REASSIGN, "Task Management: Task reassignment not supported"},
13175a17f3cSagc 	{ISCSI_STATUS_FUNCTION_UNSUPPORTED, "Task Management: Function unsupported"},
13275a17f3cSagc 	{ISCSI_STATUS_FUNCTION_NOT_AUTHORIZED, "Task Management: Function not authorized"},
13375a17f3cSagc 	{ISCSI_STATUS_FUNCTION_REJECTED, "Task Management: Function rejected"},
13475a17f3cSagc 	{ISCSI_STATUS_UNKNOWN_REASON, "Task Management: Unknown reason code"},
13575a17f3cSagc 	{ISCSI_STATUS_DUPLICATE_ID, "Duplicate ID"},
13675a17f3cSagc 	{ISCSI_STATUS_INVALID_ID, "ID not found"},
13775a17f3cSagc 	{ISCSI_STATUS_TARGET_LOGOUT, "Target requested logout"},
13875a17f3cSagc 	{ISCSI_STATUS_LOGOUT_CID_NOT_FOUND, "Logout error: Connection ID not found"},
13975a17f3cSagc 	{ISCSI_STATUS_LOGOUT_RECOVERY_NS, "Logout error: Recovery not supported"},
14075a17f3cSagc 	{ISCSI_STATUS_LOGOUT_ERROR, "Logout error: Unknown reason"},
14175a17f3cSagc 
14275a17f3cSagc 	{ISCSID_STATUS_LIST_EMPTY, "The list is empty"},
14375a17f3cSagc 	{ISCSID_STATUS_DUPLICATE_NAME, "The specified name is not unique"},
14475a17f3cSagc 	{ISCSID_STATUS_GENERAL_ERROR, "A non-specific error occurred"},
14575a17f3cSagc 	{ISCSID_STATUS_CONNECT_ERROR, "Failed to connect to target"},
14675a17f3cSagc 	{ISCSID_STATUS_NO_RESOURCES, "Could not allocate resources (e.g. memory)"},
14775a17f3cSagc 	{ISCSID_STATUS_INVALID_SESSION_ID, "Session ID not found"},
14875a17f3cSagc 	{ISCSID_STATUS_INVALID_CONNECTION_ID, "Connection ID not found"},
14975a17f3cSagc 	{ISCSID_STATUS_NOTIMPL, "Feature not implemented"},
15075a17f3cSagc 	{ISCSID_STATUS_SOCKET_ERROR, "Failed to create socket"},
15175a17f3cSagc 	{ISCSID_STATUS_PARAMETER_MISSING, "A required parameter is missing"},
15275a17f3cSagc 	{ISCSID_STATUS_PARAMETER_INVALID, "Request parameter is invalid"},
15375a17f3cSagc 	{ISCSID_STATUS_NO_INITIATOR_NAME, "Initiator name was not set"},
15475a17f3cSagc 	{ISCSID_STATUS_TIMEOUT, "Request timed out"},
15575a17f3cSagc 	{ISCSID_STATUS_DRIVER_NOT_LOADED, "iSCSI Driver not loaded"},
15675a17f3cSagc 	{ISCSID_STATUS_INVALID_REQUEST, "Unknown request code"},
15775a17f3cSagc 	{ISCSID_STATUS_INVALID_PORTAL_ID, "Portal ID not found"},
15875a17f3cSagc 	{ISCSID_STATUS_INVALID_TARGET_ID, "Target ID not found"},
15975a17f3cSagc 	{ISCSID_STATUS_NOT_FOUND, "Requested item not found"},
16075a17f3cSagc 	{ISCSID_STATUS_HOST_NOT_FOUND, "Target address not found"},
16175a17f3cSagc 	{ISCSID_STATUS_HOST_TRY_AGAIN, "Target address retrieval failed, try again later"},
16275a17f3cSagc 	{ISCSID_STATUS_HOST_ERROR, "Target address invalid"},
16375a17f3cSagc 	{ISCSID_STATUS_NO_TARGETS_FOUND, "No targets found"},
16475a17f3cSagc 	{ISCSID_STATUS_INVALID_ISNS_ID, "iSNS ID not found"},
16575a17f3cSagc 	{ISCSID_STATUS_ISNS_ERROR, "Problem connecting to iSNS"},
16675a17f3cSagc 	{ISCSID_STATUS_ISNS_SERVER_ERROR, "iSNS server returned garbage"},
16775a17f3cSagc 	{ISCSID_STATUS_DUPLICATE_ENTRY, "The entry already exists"},
16875a17f3cSagc 	{ISCSID_STATUS_INVALID_INITIATOR_ID, "Initiator ID not found"},
16975a17f3cSagc 	{ISCSID_STATUS_INITIATOR_BIND_ERROR, "Bind to initiator portal failed"},
17075a17f3cSagc 
17175a17f3cSagc 	{0, NULL}
17275a17f3cSagc };
17375a17f3cSagc 
17475a17f3cSagc /* -------------------------------------------------------------------------- */
17575a17f3cSagc /* local variables */
17675a17f3cSagc 
17775a17f3cSagc static struct sockaddr_un	daemon_name;	/* daemon socket name */
17875a17f3cSagc 
17975a17f3cSagc static char		 	sockdir[MAXPATHLEN]; /* where myname lives */
18075a17f3cSagc static struct sockaddr_un	myname;		/* my socket name */
18175a17f3cSagc static int			sock;		/* the socket */
18275a17f3cSagc 
18375a17f3cSagc static char *cmdname;			/* pointer to command name for error msgs */
18475a17f3cSagc 
18575a17f3cSagc /* global variables */
18675a17f3cSagc 
18775a17f3cSagc uint8_t buf[BUF_SIZE];			/* buffer for daemon comm and driver I/O */
18875a17f3cSagc 
18975a17f3cSagc int driver;						/* driver handle */
19075a17f3cSagc 
19175a17f3cSagc /* -------------------------------------------------------------------------- */
19275a17f3cSagc 
19375a17f3cSagc #define progname  getprogname()
19475a17f3cSagc 
19575a17f3cSagc 
19675a17f3cSagc /*
19775a17f3cSagc  * bye:
19875a17f3cSagc  *    Cleanup and exit. Does not return.
19975a17f3cSagc */
20075a17f3cSagc 
201549f044eSjoerg __dead static void
bye(void)20275a17f3cSagc bye(void)
20375a17f3cSagc {
20475a17f3cSagc 	close(sock);
20575a17f3cSagc 	(void) unlink(myname.sun_path);
20675a17f3cSagc 	(void) rmdir(sockdir);
20775a17f3cSagc 	exit(EXIT_FAILURE);
20875a17f3cSagc }
20975a17f3cSagc 
21075a17f3cSagc 
21175a17f3cSagc /*
21275a17f3cSagc  * arg_error:
21375a17f3cSagc  *    Display error message about an invalid argument, exit.
21475a17f3cSagc  *
21575a17f3cSagc  *    Parameters:
21675a17f3cSagc  *       argp        Argument value
21775a17f3cSagc  *       fmt         Error message
21875a17f3cSagc  *       ...         additional output arguments
21975a17f3cSagc  *
22075a17f3cSagc  *    Does not return.
22175a17f3cSagc */
22275a17f3cSagc 
22375a17f3cSagc void
arg_error(char * argp,const char * fmt,...)22475a17f3cSagc arg_error(char *argp, const char *fmt, ...)
22575a17f3cSagc {
22675a17f3cSagc 	va_list args;
22775a17f3cSagc 	char	lbuf[BUF_SIZE];
22875a17f3cSagc 
22975a17f3cSagc 	va_start(args, fmt);
23075a17f3cSagc 	vsnprintf(lbuf, sizeof(lbuf), fmt, args);
23175a17f3cSagc 	fprintf(stderr, "%s: %s: Invalid option at or near '%s': %s\n",
23275a17f3cSagc 		progname, cmdname, argp, lbuf);
23375a17f3cSagc 	bye();
23475a17f3cSagc }
23575a17f3cSagc 
23675a17f3cSagc 
23775a17f3cSagc /*
23875a17f3cSagc  * arg_missing:
23975a17f3cSagc  *    Display error message about a missing argument, exit.
24075a17f3cSagc  *
24175a17f3cSagc  *    Parameters:
24275a17f3cSagc  *       arg      Argument name
24375a17f3cSagc  *
24475a17f3cSagc  *    Does not return.
24575a17f3cSagc  */
24675a17f3cSagc 
24775a17f3cSagc void
arg_missing(const char * arg)24875a17f3cSagc arg_missing(const char *arg)
24975a17f3cSagc {
25075a17f3cSagc 	warnx("%s: Missing argument: %s", cmdname, arg);
25175a17f3cSagc 	bye();
25275a17f3cSagc }
25375a17f3cSagc 
25475a17f3cSagc 
25575a17f3cSagc /*
25675a17f3cSagc  * io_error:
25775a17f3cSagc  *    Display error message about an I/O error (includes system error code)
25875a17f3cSagc  *
25975a17f3cSagc  *    Parameters:
26075a17f3cSagc  *       fmt         format string
26175a17f3cSagc  *       ...         additional output arguments
26275a17f3cSagc  *
26375a17f3cSagc  *    Does not return.
26475a17f3cSagc  */
26575a17f3cSagc 
26675a17f3cSagc void
io_error(const char * fmt,...)26775a17f3cSagc io_error(const char *fmt, ...)
26875a17f3cSagc {
26975a17f3cSagc 	va_list args;
27075a17f3cSagc 	char	lbuf[BUF_SIZE];
27175a17f3cSagc 
27275a17f3cSagc 	va_start(args, fmt);
27375a17f3cSagc 	vsnprintf(lbuf, sizeof(lbuf), fmt, args);
27475a17f3cSagc 	fprintf(stderr, "%s: %s: %s: %s\n",
27575a17f3cSagc 		progname, cmdname, lbuf, strerror(errno));
27675a17f3cSagc 	bye();
27775a17f3cSagc }
27875a17f3cSagc 
27975a17f3cSagc 
28075a17f3cSagc /*
28175a17f3cSagc  * gen_error:
28275a17f3cSagc  *    Display general error message.
28375a17f3cSagc  *
28475a17f3cSagc  *    Parameters:
28575a17f3cSagc  *       fmt         format string
28675a17f3cSagc  *       ...         additional output arguments
28775a17f3cSagc  *
28875a17f3cSagc  *    Does not return.
28975a17f3cSagc  */
29075a17f3cSagc 
29175a17f3cSagc void
gen_error(const char * fmt,...)29275a17f3cSagc gen_error(const char *fmt, ...)
29375a17f3cSagc {
29475a17f3cSagc 	va_list args;
29575a17f3cSagc 	char	lbuf[BUF_SIZE];
29675a17f3cSagc 
29775a17f3cSagc 	va_start(args, fmt);
29875a17f3cSagc 	vsnprintf(lbuf, sizeof(lbuf), fmt, args);
29975a17f3cSagc 	fprintf(stderr, "%s: %s: %s\n", progname, cmdname, lbuf);
30075a17f3cSagc 	bye();
30175a17f3cSagc }
30275a17f3cSagc 
30375a17f3cSagc 
30475a17f3cSagc /*
30575a17f3cSagc  * check_extra_args:
30675a17f3cSagc  *    Display error message & exit if there is an unrecognized argument.
30775a17f3cSagc  *
30875a17f3cSagc  *    Parameters:
30975a17f3cSagc  *       argc     Argument count
31075a17f3cSagc  *       argv     Argument value array.
31175a17f3cSagc  *
31275a17f3cSagc  *    Does not return if an extra arg is found.
31375a17f3cSagc  */
31475a17f3cSagc 
31575a17f3cSagc void
check_extra_args(int argc,char ** argv)31675a17f3cSagc check_extra_args(int argc, char **argv)
31775a17f3cSagc {
31875a17f3cSagc 	int i;
31975a17f3cSagc 
32075a17f3cSagc 	for (i = 0; i < argc; i++)
32175a17f3cSagc 		if (argv[i] != NULL) {
32275a17f3cSagc 			warnx("%s: Unrecognized argument '%s'", cmdname, argv[i]);
32375a17f3cSagc 			bye();
32475a17f3cSagc 		}
32575a17f3cSagc }
32675a17f3cSagc 
32775a17f3cSagc 
32875a17f3cSagc /*
32975a17f3cSagc  * status_error:
33075a17f3cSagc  *    Display error message for status returned by daemon or driver, exit.
33175a17f3cSagc  *
33275a17f3cSagc  *    Parameters:
33375a17f3cSagc  *          n     Status code.
33475a17f3cSagc  *
33575a17f3cSagc  *    Does not return.
33675a17f3cSagc  */
33775a17f3cSagc 
33875a17f3cSagc void
status_error(unsigned n)33975a17f3cSagc status_error(unsigned n)
34075a17f3cSagc {
34175a17f3cSagc 	int i;
34275a17f3cSagc 
34375a17f3cSagc 	for (i = 0; status_msg[i].code; i++)
34475a17f3cSagc 		if (status_msg[i].code == n)
34575a17f3cSagc 			break;
34675a17f3cSagc 
34775a17f3cSagc 	if (status_msg[i].code)
34875a17f3cSagc 		warnx("%s: %s", cmdname, status_msg[i].str);
34975a17f3cSagc 	else
35075a17f3cSagc 		warnx("%s: Undefined error code %d", cmdname, n);
35175a17f3cSagc 
35275a17f3cSagc 	bye();
35375a17f3cSagc }
35475a17f3cSagc 
35575a17f3cSagc 
35675a17f3cSagc /*
35775a17f3cSagc  * status_error_slist:
35875a17f3cSagc  *    Display error message for status returned by daemon or driver, but
35975a17f3cSagc  *	  replace a "list is empty" code by an "ID not found" code, exit.
36075a17f3cSagc  *
36175a17f3cSagc  *    Parameters:
36275a17f3cSagc  *          n     Status code.
36375a17f3cSagc  *
36475a17f3cSagc  *    Does not return.
36575a17f3cSagc  */
36675a17f3cSagc 
36775a17f3cSagc void
status_error_slist(unsigned n)36875a17f3cSagc status_error_slist(unsigned n)
36975a17f3cSagc {
37075a17f3cSagc 	if (n == ISCSI_STATUS_LIST_EMPTY || n == ISCSID_STATUS_LIST_EMPTY)
37175a17f3cSagc 		n = ISCSI_STATUS_INVALID_ID;
37275a17f3cSagc 	status_error (n);
37375a17f3cSagc }
37475a17f3cSagc 
37575a17f3cSagc 
37675a17f3cSagc /*
37775a17f3cSagc  * get_response:
37875a17f3cSagc  *    Read the response from the daemon.
37975a17f3cSagc  *
38075a17f3cSagc  *    Parameters:
38175a17f3cSagc  *          temp  If TRUE, the response is dynamically allocated (so it is not
38275a17f3cSagc  *                overwritten by further requests or responses).
38375a17f3cSagc  *
38475a17f3cSagc  *    Returns:
38575a17f3cSagc  *          Pointer to the response.
38675a17f3cSagc  *
38775a17f3cSagc  *    Notes:
38875a17f3cSagc  *       This routine allocates an extra integer to mark whether the returned
38975a17f3cSagc  *       buffer is dynamic or static. This marker is one int before the
39075a17f3cSagc  *       returned pointer.
39175a17f3cSagc  */
39275a17f3cSagc 
39375a17f3cSagc iscsid_response_t *
get_response(int temp)39475a17f3cSagc get_response(int temp)
39575a17f3cSagc {
3961309a94fSchristos 	ssize_t ret;
3971309a94fSchristos 	size_t len;
39875a17f3cSagc 	iscsid_response_t *rsp;
39975a17f3cSagc 	int *pbuf;
40075a17f3cSagc 
4011309a94fSchristos 	pbuf = (int *)(void *)buf;
4021309a94fSchristos 	rsp = (iscsid_response_t *)(void *)&pbuf[1];
40375a17f3cSagc 	*pbuf = 0;
40475a17f3cSagc 
40575a17f3cSagc 	/* get size of response */
40675a17f3cSagc 	len = sizeof(iscsid_response_t);
40775a17f3cSagc 	ret = recv(sock, rsp, len, MSG_PEEK | MSG_WAITALL);
4081309a94fSchristos 	if ((size_t)ret != len)
40975a17f3cSagc 		io_error("Receiving daemon data");
41075a17f3cSagc 
41175a17f3cSagc 	len += rsp->parameter_length;
41275a17f3cSagc 
41375a17f3cSagc 	/*
41475a17f3cSagc 	   if a temp buffer has been requested, or if the response is too large
41575a17f3cSagc 	   to fit into the static buffer, alloc a temp buffer.
41675a17f3cSagc 	 */
41775a17f3cSagc 
41875a17f3cSagc 	temp = temp || (len > (int)(sizeof(buf) - sizeof(int)));
41975a17f3cSagc 
42075a17f3cSagc 	if (temp) {
42175a17f3cSagc 		if (NULL == (pbuf = (int *) malloc(len + sizeof(int))))
4222360984dSchristos 			gen_error("Can't allocate response buffer (%zu bytes)",
42375a17f3cSagc 				len + sizeof(int));
42475a17f3cSagc 
4251309a94fSchristos 		rsp = (iscsid_response_t *)(void *)&pbuf[1];
42675a17f3cSagc 		*pbuf = 1;
42775a17f3cSagc 	}
42875a17f3cSagc 	/* get the complete response */
42975a17f3cSagc 
43075a17f3cSagc 	ret = recv(sock, rsp, len, MSG_WAITALL);
4311309a94fSchristos 	if ((size_t)ret != len)
43275a17f3cSagc 		io_error("Receiving daemon data");
43375a17f3cSagc 
43475a17f3cSagc 	return rsp;
43575a17f3cSagc }
43675a17f3cSagc 
43775a17f3cSagc 
43875a17f3cSagc /*
43975a17f3cSagc  * free_response:
44075a17f3cSagc  *    If the response buffer was dynamically allocated, free it.
44175a17f3cSagc  *
44275a17f3cSagc  *    Parameters:
44375a17f3cSagc  *          rsp   The response buffer.
44475a17f3cSagc  *                The dynamic allocation marker is the int preceding
44575a17f3cSagc  *                this address.
44675a17f3cSagc  */
44775a17f3cSagc 
44875a17f3cSagc void
free_response(iscsid_response_t * rsp)44975a17f3cSagc free_response(iscsid_response_t * rsp)
45075a17f3cSagc {
45175a17f3cSagc 	int *pbuf;
45275a17f3cSagc 
4531309a94fSchristos 	pbuf = ((int *)(void *)rsp) - 1;
45475a17f3cSagc 	if (*pbuf)
45575a17f3cSagc 		free(pbuf);
45675a17f3cSagc }
45775a17f3cSagc 
45875a17f3cSagc 
45975a17f3cSagc /*
46075a17f3cSagc  * send_request:
46175a17f3cSagc  *    Send a request to the daemon.
46275a17f3cSagc  *
46375a17f3cSagc  *    Parameters:
46475a17f3cSagc  *          request  The request code.
46575a17f3cSagc  *          par_len  The parameter length.
46675a17f3cSagc  *          par      The parameter.
46775a17f3cSagc  */
46875a17f3cSagc 
46975a17f3cSagc void
send_request(unsigned request,size_t par_len,void * par)4701309a94fSchristos send_request(unsigned request, size_t par_len, void *par)
47175a17f3cSagc {
47275a17f3cSagc 	iscsid_request_t *req;
4731309a94fSchristos 	size_t len;
4741309a94fSchristos 	ssize_t ret;
4751309a94fSchristos 	int req_temp;
47675a17f3cSagc 
47775a17f3cSagc 	len = sizeof(iscsid_request_t) + par_len;
47875a17f3cSagc 
47975a17f3cSagc 	/* alloc buffer if static one is too small to hold request */
4801309a94fSchristos 	req_temp = len > sizeof(buf);
48175a17f3cSagc 
48275a17f3cSagc 	if (req_temp) {
48375a17f3cSagc 		req = malloc(len);
48475a17f3cSagc 		if (req == NULL)
4851309a94fSchristos 			gen_error("Out of memory allocating %zu bytes\n", len);
48675a17f3cSagc 	} else
4871309a94fSchristos 		req = (iscsid_request_t *)(void *)buf;
48875a17f3cSagc 
48975a17f3cSagc 	/* setup request */
49075a17f3cSagc 	req->request = request;
4911309a94fSchristos 	req->parameter_length = (uint32_t)par_len;
49275a17f3cSagc 	if (par_len)
49375a17f3cSagc 		memcpy(req->parameter, par, par_len);
49475a17f3cSagc 
49575a17f3cSagc 	/* and send it out */
4961309a94fSchristos 	ret = sendto(sock, req, len, 0, (struct sockaddr *)(void *)&daemon_name,
4971309a94fSchristos 				 (socklen_t)sizeof(struct sockaddr_un));
4981309a94fSchristos 	if ((size_t)ret != len) {
49975a17f3cSagc 		io_error("Sending daemon message");
50075a17f3cSagc 	}
50175a17f3cSagc 	if (req_temp)
50275a17f3cSagc 		free(req);
50375a17f3cSagc }
50475a17f3cSagc 
50575a17f3cSagc 
50675a17f3cSagc /*
50775a17f3cSagc  * main:
50875a17f3cSagc  *    check command, init driver handle and socket, dispatch command.
50975a17f3cSagc  *
51075a17f3cSagc  *    Parameter:  argc, argv - passed on to commands with offset of 2, so
51175a17f3cSagc  *                argv [0] is first argument after command verb.
51275a17f3cSagc  *
51375a17f3cSagc  *    Returns:
51475a17f3cSagc  *          Whatever the command handler returns, which is currently always 0.
51575a17f3cSagc  */
51675a17f3cSagc 
51775a17f3cSagc int
main(int argc,char ** argv)51875a17f3cSagc main(int argc, char **argv)
51975a17f3cSagc {
52075a17f3cSagc 	command_t	*c;
52175a17f3cSagc 	int		 res;
52275a17f3cSagc 	int		 i;
52375a17f3cSagc 
52475a17f3cSagc 	(void) snprintf(sockdir, sizeof(sockdir), "/tmp/iscsictl.XXXXXX");
52575a17f3cSagc 	while ((i = getopt(argc, argv, "d:")) != -1) {
52675a17f3cSagc 		switch(i) {
52775a17f3cSagc 		case 'd':
52875a17f3cSagc 			(void) snprintf(sockdir, sizeof(sockdir), "%s", optarg);
52975a17f3cSagc 			break;
53075a17f3cSagc 		default:
53175a17f3cSagc 			break;
53275a17f3cSagc 		}
53375a17f3cSagc 	}
53475a17f3cSagc 	if (argc - optind  < 1) {
53575a17f3cSagc 		errx(EXIT_FAILURE, "Usage: %s <command> <options>, see manual for details.",
53675a17f3cSagc 			progname);
53775a17f3cSagc 	}
53875a17f3cSagc 
53975a17f3cSagc 	cmdname = argv[optind];
54075a17f3cSagc 
54175a17f3cSagc 	for (c = cmds; c->cmd != NULL; c++) {
54275a17f3cSagc 		if (strcmp(c->cmd, cmdname) == 0) {
54375a17f3cSagc 			break;
54475a17f3cSagc 		}
54575a17f3cSagc 	}
54675a17f3cSagc 	if (c->cmd == NULL) {
54775a17f3cSagc 		errx(EXIT_FAILURE, "Unknown command: '%s'", cmdname);
54875a17f3cSagc 	}
549*44f0e146Sjoerg 	if ((driver = open(DEVICE, O_RDONLY)) < 0)
550*44f0e146Sjoerg 		err(EXIT_FAILURE, "Opening " DEVICE);
55175a17f3cSagc 
55275a17f3cSagc 	sock = socket(AF_UNIX, SOCK_DGRAM, 0);
55375a17f3cSagc 	if (sock < 0)
55475a17f3cSagc 		err(EXIT_FAILURE, "opening datagram socket");
55575a17f3cSagc 
55675a17f3cSagc 	/* bind socket to unique name */
55775a17f3cSagc 	if (mkdtemp(sockdir) == NULL) {
55875a17f3cSagc 		errx(EXIT_FAILURE, "can't create iscsictl dir '%s'", sockdir);
55975a17f3cSagc 	}
56075a17f3cSagc 	myname.sun_family = AF_UNIX;
56175a17f3cSagc 	(void) snprintf(myname.sun_path, sizeof(myname.sun_path), "%s/socket", sockdir);
5621309a94fSchristos 	if (bind(sock, (struct sockaddr *)(void *)&myname,
5631309a94fSchristos 	    (socklen_t)sizeof(struct sockaddr_un)) < 0) {
56475a17f3cSagc 		io_error("Binding name to datagram socket");
56575a17f3cSagc 	}
56675a17f3cSagc 	daemon_name.sun_family = AF_UNIX;
56775a17f3cSagc 	strlcpy(daemon_name.sun_path, ISCSID_SOCK_NAME,
56875a17f3cSagc 		sizeof(daemon_name.sun_path));
56975a17f3cSagc 
57075a17f3cSagc 	/* dispatch command */
57176ad6f22Sagc 	res = (*c->proc)(argc - optind - 1, &argv[optind + 1]);
57275a17f3cSagc 
57375a17f3cSagc 	/* cleanup */
57475a17f3cSagc 	close(sock);
57575a17f3cSagc 	unlink(myname.sun_path);
57675a17f3cSagc 	rmdir(sockdir);
57775a17f3cSagc 
57875a17f3cSagc 	return res;
57975a17f3cSagc }
580