target.c revision 1.5
1/*
2 * IMPORTANT:  READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
3 * By downloading, copying, installing or using the software you agree
4 * to this license.  If you do not agree to this license, do not
5 * download, install, copy or use the software.
6 *
7 * Intel License Agreement
8 *
9 * Copyright (c) 2000, Intel Corporation
10 * All rights reserved.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 *
16 * -Redistributions of source code must retain the above copyright
17 *  notice, this list of conditions and the following disclaimer.
18 *
19 * -Redistributions in binary form must reproduce the above copyright
20 *  notice, this list of conditions and the following disclaimer in the
21 *  documentation and/or other materials provided with the
22 *  distribution.
23 *
24 * -The name of Intel Corporation may not be used to endorse or
25 *  promote products derived from this software without specific prior
26 *  written permission.
27 *
28 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
31 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL INTEL
32 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
33 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
34 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
35 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
36 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
37 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
38 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39 * SUCH DAMAGE.
40 */
41#include "config.h"
42
43#include <sys/types.h>
44#include <sys/param.h>
45
46#include <assert.h>
47#include <stdlib.h>
48
49#ifdef HAVE_NETINET_TCP_H
50#include <netinet/tcp.h>
51#endif
52
53#ifdef HAVE_SYS_UIO_H
54#include <sys/uio.h>
55#endif
56
57#ifdef HAVE_SYS_SOCKET_H
58#include <sys/socket.h>
59#endif
60
61#ifdef HAVE_NETINET_IN_H
62#include <netinet/in.h>
63#endif
64
65#ifdef HAVE_STRING_H
66#include <string.h>
67#endif
68
69#ifdef HAVE_SIGNAL_H
70#include <signal.h>
71#endif
72
73#ifdef HAVE_SYSLOG_H
74#include <syslog.h>
75#endif
76
77#ifdef HAVE_ERRNO_H
78#include <errno.h>
79#endif
80
81#ifdef HAVE_NETDB_H
82#include <netdb.h>
83#endif
84
85#ifdef HAVE_ARPA_INET_H
86#include <arpa/inet.h>
87#endif
88
89#ifdef HAVE_INTTYPES_H
90#include <inttypes.h>
91#endif
92
93
94#include "iscsiprotocol.h"
95#include "conffile.h"
96#include "storage.h"
97#include "target.h"
98#include "device.h"
99#include "iscsi-md5.h"
100#include "parameters.h"
101#include "iscsi.h"
102
103enum {
104	TARGET_SHUT_DOWN     = 0,
105	TARGET_INITIALIZING  = 1,
106	TARGET_INITIALIZED   = 2,
107	TARGET_SHUTTING_DOWN = 3
108};
109
110/***********
111 * Private *
112 ***********/
113
114static target_session_t	*g_session;
115static iscsi_queue_t	g_session_q;
116static iscsi_mutex_t	g_session_q_mutex;
117
118/*********************
119 * Private Functions *
120 *********************/
121
122static char *
123get_iqn(target_session_t *sess, uint32_t t, char *buf, size_t size)
124{
125	targv_t	*targv;
126
127	targv = sess->target->lunv;
128	if (targv->v[t].iqn != NULL) {
129		(void) strlcpy(buf, targv->v[t].iqn, size);
130		return buf;
131	}
132	(void) snprintf(buf, size, "%s:%s",
133			iscsi_target_getvar(sess->target, "iqn"),
134			targv->v[t].target);
135	return buf;
136}
137
138static int
139reject_t(target_session_t * sess, uint8_t *header, uint8_t reason)
140{
141	iscsi_reject_t  reject;
142	uint8_t   rsp_header[ISCSI_HEADER_LEN];
143
144	iscsi_err(__FILE__, __LINE__, "reject %x\n", reason);
145	reject.reason = reason;
146	reject.length = ISCSI_HEADER_LEN;
147	reject.StatSN = ++(sess->StatSN);
148	reject.ExpCmdSN = sess->ExpCmdSN;
149	reject.MaxCmdSN = sess->MaxCmdSN;
150	reject.DataSN = 0;	/* SNACK not yet implemented */
151
152	if (iscsi_reject_encap(rsp_header, &reject) != 0) {
153		iscsi_err(__FILE__, __LINE__,
154				"iscsi_reject_encap() failed\n");
155		return -1;
156	}
157	if (iscsi_sock_send_header_and_data(sess->sock, rsp_header,
158			ISCSI_HEADER_LEN, header, ISCSI_HEADER_LEN, 0) !=
159			2 * ISCSI_HEADER_LEN) {
160		iscsi_err(__FILE__, __LINE__,
161			"iscsi_sock_send_header_and_data() failed\n");
162		return -1;
163	}
164	return 0;
165}
166
167static int
168scsi_command_t(target_session_t *sess, uint8_t *header)
169{
170	iscsi_scsi_cmd_args_t	scsi_cmd;
171	iscsi_read_data_t	data;
172	iscsi_scsi_rsp_t	scsi_rsp;
173	target_cmd_t		cmd;
174	uint32_t		DataSN = 0;
175	uint8_t			rsp_header[ISCSI_HEADER_LEN];
176
177	(void) memset(&scsi_cmd, 0x0, sizeof(scsi_cmd));
178	if (iscsi_scsi_cmd_decap(header, &scsi_cmd) != 0) {
179		iscsi_err(__FILE__, __LINE__,
180				"iscsi_scsi_cmd_decap() failed\n");
181		return -1;
182	}
183	iscsi_trace(TRACE_ISCSI_DEBUG,
184		"session %d: SCSI Command (CmdSN %u, op %#x)\n",
185		sess->id, scsi_cmd.CmdSN, scsi_cmd.cdb[0]);
186
187	/* For Non-immediate commands, the CmdSN should be between ExpCmdSN  */
188	/* and MaxCmdSN, inclusive of both.  Otherwise, ignore the command */
189	if (!scsi_cmd.immediate &&
190	    (scsi_cmd.CmdSN < sess->ExpCmdSN ||
191	     scsi_cmd.CmdSN > sess->MaxCmdSN)) {
192		iscsi_err(__FILE__, __LINE__,
193			"CmdSN(%d) of SCSI Command not valid, "
194			"ExpCmdSN(%d) MaxCmdSN(%d). Ignoring the command\n",
195			scsi_cmd.CmdSN, sess->ExpCmdSN, sess->MaxCmdSN);
196		return 0;
197	}
198	/* Arg check.   */
199	scsi_cmd.attr = 0;	/* Temp fix FIXME */
200	/*
201	 * RETURN_NOT_EQUAL("ATTR (FIX ME)", scsi_cmd.attr, 0, NO_CLEANUP,
202	 * -1);
203	 */
204
205	/* Check Numbering */
206
207	if (scsi_cmd.CmdSN != sess->ExpCmdSN) {
208		iscsi_warn(__FILE__, __LINE__,
209			"Expected CmdSN %d, got %d. "
210			"(ignoring and resetting expectations)\n",
211			sess->ExpCmdSN, scsi_cmd.CmdSN);
212		sess->ExpCmdSN = scsi_cmd.CmdSN;
213	}
214	/* Check Transfer Lengths */
215	if (sess->sess_params.first_burst_length
216	    && (scsi_cmd.length > sess->sess_params.first_burst_length)) {
217		iscsi_err(__FILE__, __LINE__,
218			"scsi_cmd.length (%u) > FirstBurstLength (%u)\n",
219			scsi_cmd.length, sess->sess_params.first_burst_length);
220		scsi_cmd.status = 0x02;
221		scsi_cmd.length = 0;
222		goto response;
223	}
224	if (sess->sess_params.max_dataseg_len &&
225	    scsi_cmd.length > sess->sess_params.max_dataseg_len) {
226		iscsi_err(__FILE__, __LINE__,
227			"scsi_cmd.length (%u) > MaxRecvDataSegmentLength "
228			"(%u)\n",
229			scsi_cmd.length, sess->sess_params.max_dataseg_len);
230		return -1;
231	}
232
233#if 0
234	/* commented out in original Intel reference code */
235	if (scsi_cmd.final && scsi_cmd.output) {
236		RETURN_NOT_EQUAL("Length", scsi_cmd.length,
237			scsi_cmd.trans_len, NO_CLEANUP, -1);
238	}
239#endif
240
241	/* Read AHS.  Need to optimize/clean this.   */
242	/* We should not be calling malloc(). */
243	/* We need to check for properly formated AHS segments. */
244
245	if (scsi_cmd.ahs_len) {
246		uint32_t        ahs_len;
247		uint8_t  *ahs_ptr;
248		uint8_t   ahs_type;
249
250		scsi_cmd.ahs = NULL;
251		iscsi_trace(TRACE_ISCSI_DEBUG,
252				"reading %u bytes AHS\n", scsi_cmd.ahs_len);
253		scsi_cmd.ahs = iscsi_malloc_atomic((unsigned)scsi_cmd.ahs_len);
254		if (scsi_cmd.ahs == NULL) {
255			iscsi_err(__FILE__, __LINE__,
256				"iscsi_malloc_atomic() failed\n");
257			return -1;
258		}
259#define AHS_CLEANUP do {						\
260	if (scsi_cmd.ahs != NULL) {					\
261		iscsi_free_atomic(scsi_cmd.ahs);			\
262	}								\
263} while (/* CONSTCOND */ 0)
264		if (iscsi_sock_msg(sess->sock, 0, (unsigned)scsi_cmd.ahs_len,
265				scsi_cmd.ahs, 0) != scsi_cmd.ahs_len) {
266			iscsi_err(__FILE__, __LINE__,
267				"iscsi_sock_msg() failed\n");
268			AHS_CLEANUP;
269			return -1;
270		}
271		iscsi_trace(TRACE_ISCSI_DEBUG,
272				"read %u bytes AHS\n", scsi_cmd.ahs_len);
273		for (ahs_ptr = scsi_cmd.ahs;
274		     ahs_ptr < (scsi_cmd.ahs + scsi_cmd.ahs_len - 1) ;
275		     ahs_ptr += ahs_len) {
276			ahs_len = ISCSI_NTOHS(*((uint16_t *) (void *)ahs_ptr));
277			if (ahs_len == 0) {
278				iscsi_err(__FILE__, __LINE__,
279				 		"Zero ahs_len\n");
280				AHS_CLEANUP;
281				return -1;
282			}
283			switch (ahs_type = *(ahs_ptr + 2)) {
284			case ISCSI_AHS_EXTENDED_CDB:
285				iscsi_trace(TRACE_ISCSI_DEBUG,
286					"Got ExtendedCDB AHS - %u bytes extra "
287					"CDB)\n", ahs_len - 1);
288				scsi_cmd.ext_cdb = ahs_ptr + 4;
289				break;
290			case ISCSI_AHS_BIDI_READ:
291				scsi_cmd.bidi_trans_len =
292					ISCSI_NTOHL(*((uint32_t *)(void *)
293							(ahs_ptr + 4)));
294				*((uint32_t *)(void *)(ahs_ptr + 4)) =
295						scsi_cmd.bidi_trans_len;
296				iscsi_trace(TRACE_ISCSI_DEBUG,
297					"Got Bidirectional Read AHS "
298					"(expected read length %u)\n",
299					scsi_cmd.bidi_trans_len);
300				break;
301			default:
302				iscsi_err(__FILE__, __LINE__,
303					"unknown AHS type %x\n", ahs_type);
304				AHS_CLEANUP;
305				return -1;
306			}
307		}
308		iscsi_trace(TRACE_ISCSI_DEBUG,
309			"done parsing %u bytes AHS\n", scsi_cmd.ahs_len);
310	} else {
311		iscsi_trace(TRACE_ISCSI_DEBUG, "no AHS to read\n");
312		scsi_cmd.ahs = NULL;
313	}
314
315	sess->ExpCmdSN++;
316	sess->MaxCmdSN++;
317
318	/* Execute cdb.  device_command() will set scsi_cmd.input if
319	* there is input data and set the length of the input to
320	* either scsi_cmd.trans_len or scsi_cmd.bidi_trans_len,
321	* depending on whether scsi_cmd.output was set.  */
322	if (scsi_cmd.input) {
323		scsi_cmd.send_data = sess->buff;
324	}
325	scsi_cmd.input = 0;
326	cmd.scsi_cmd = &scsi_cmd;
327	cmd.callback = NULL;
328	if (device_command(sess, &cmd) != 0) {
329		iscsi_err(__FILE__, __LINE__,
330				"device_command() failed\n");
331		AHS_CLEANUP;
332		return -1;
333	}
334	/* Send any input data */
335
336	scsi_cmd.bytes_sent = 0;
337	if (!scsi_cmd.status && scsi_cmd.input) {
338		struct iovec    sg_singleton;
339		struct iovec   *sg, *sg_orig, *sg_new = NULL;
340		int             sg_len_orig, sg_len;
341		uint32_t        offset, trans_len;
342		int             fragment_flag = 0;
343		int             offset_inc;
344#define SG_CLEANUP do {							\
345	if (fragment_flag) {						\
346		iscsi_free_atomic(sg_new);				\
347	}								\
348} while (/* CONSTCOND */ 0)
349		if (scsi_cmd.output) {
350			iscsi_trace(TRACE_ISCSI_DEBUG,
351				"sending %u bytes bi-directional input data\n",
352				scsi_cmd.bidi_trans_len);
353			trans_len = scsi_cmd.bidi_trans_len;
354		} else {
355			trans_len = scsi_cmd.trans_len;
356		}
357		iscsi_trace(TRACE_ISCSI_DEBUG,
358			"sending %u bytes input data as separate PDUs\n",
359			trans_len);
360
361		if (scsi_cmd.send_sg_len) {
362			sg_orig = (struct iovec *)(void *)scsi_cmd.send_data;
363			sg_len_orig = scsi_cmd.send_sg_len;
364		} else {
365			sg_len_orig = 1;
366			sg_singleton.iov_base = scsi_cmd.send_data;
367			sg_singleton.iov_len = trans_len;
368			sg_orig = &sg_singleton;
369		}
370		sg = sg_orig;
371		sg_len = sg_len_orig;
372
373		offset_inc = (sess->sess_params.max_dataseg_len) ?
374			sess->sess_params.max_dataseg_len : trans_len;
375
376		for (offset = 0; offset < trans_len; offset += offset_inc) {
377			(void) memset(&data, 0x0, sizeof(data));
378			data.length = (sess->sess_params.max_dataseg_len) ?
379				MIN(trans_len - offset,
380					sess->sess_params.max_dataseg_len) :
381				trans_len - offset;
382			if (data.length != trans_len) {
383				if (!fragment_flag) {
384					sg_new = iscsi_malloc_atomic(sizeof(struct iovec) * sg_len_orig);
385					if (sg_new == NULL) {
386						iscsi_err(__FILE__, __LINE__, "iscsi_malloc_atomic() failed\n");
387						AHS_CLEANUP;
388						return -1;
389					}
390					fragment_flag++;
391				}
392				sg = sg_new;
393				sg_len = sg_len_orig;
394				(void) memcpy(sg, sg_orig, sizeof(struct iovec) * sg_len_orig);
395				if (modify_iov(&sg, &sg_len, offset, data.length) != 0) {
396					iscsi_err(__FILE__, __LINE__, "modify_iov() failed\n");
397					SG_CLEANUP;
398					AHS_CLEANUP;
399					return -1;
400				}
401			}
402			iscsi_trace(TRACE_ISCSI_DEBUG, "sending read data PDU (offset %u, len %u)\n", offset, data.length);
403			if (offset + data.length == trans_len) {
404				data.final = 1;
405
406				if (sess->UsePhaseCollapsedRead) {
407					data.status = 1;
408					data.status = scsi_cmd.status;
409					data.StatSN = ++(sess->StatSN);
410					iscsi_trace(TRACE_ISCSI_DEBUG, "status %#x collapsed into last data PDU\n", data.status);
411				} else {
412					iscsi_trace(TRACE_ISCSI_DEBUG, "NOT collapsing status with last data PDU\n");
413				}
414			} else if (offset + data.length > trans_len) {
415				iscsi_err(__FILE__, __LINE__, "offset+data.length > trans_len??\n");
416				SG_CLEANUP;
417				AHS_CLEANUP;
418				return -1;
419			}
420			data.task_tag = scsi_cmd.tag;
421			data.ExpCmdSN = sess->ExpCmdSN;
422			data.MaxCmdSN = sess->MaxCmdSN;
423			data.DataSN = DataSN++;
424			data.offset = offset;
425			if (iscsi_read_data_encap(rsp_header, &data) != 0) {
426				iscsi_err(__FILE__, __LINE__, "iscsi_read_data_encap() failed\n");
427				SG_CLEANUP;
428				AHS_CLEANUP;
429				return -1;
430			}
431			if ((uint32_t)iscsi_sock_send_header_and_data(sess->sock, rsp_header, ISCSI_HEADER_LEN, sg, data.length, sg_len)
432			    != ISCSI_HEADER_LEN + data.length) {
433				iscsi_err(__FILE__, __LINE__, "iscsi_sock_send_header_and_data() failed\n");
434				SG_CLEANUP;
435				AHS_CLEANUP;
436				return -1;
437			}
438			scsi_cmd.bytes_sent += data.length;
439			iscsi_trace(TRACE_ISCSI_DEBUG, "sent read data PDU ok (offset %u, len %u)\n", data.offset, data.length);
440		}
441		SG_CLEANUP;
442		iscsi_trace(TRACE_ISCSI_DEBUG, "successfully sent %u bytes read data\n", trans_len);
443	}
444	/*
445         * Send a response PDU if
446         *
447         * 1) we're not using phase collapsed input (and status was good)
448         * 2) we are using phase collapsed input, but there was no input data (e.g., TEST UNIT READY)
449         * 3) command had non-zero status and possible sense data
450         */
451response:
452	if (!sess->UsePhaseCollapsedRead || !scsi_cmd.length || scsi_cmd.status) {
453		iscsi_trace(TRACE_ISCSI_DEBUG, "sending SCSI response PDU\n");
454		(void) memset(&scsi_rsp, 0x0, sizeof(scsi_rsp));
455		scsi_rsp.length = scsi_cmd.status ? scsi_cmd.length : 0;
456		scsi_rsp.tag = scsi_cmd.tag;
457		/* If r2t send, then the StatSN is already incremented */
458		if (sess->StatSN < scsi_cmd.ExpStatSN) {
459			++sess->StatSN;
460		}
461		scsi_rsp.StatSN = sess->StatSN;
462		scsi_rsp.ExpCmdSN = sess->ExpCmdSN;
463		scsi_rsp.MaxCmdSN = sess->MaxCmdSN;
464		scsi_rsp.ExpDataSN = (!scsi_cmd.status && scsi_cmd.input) ? DataSN : 0;
465		scsi_rsp.response = 0x00;	/* iSCSI response */
466		scsi_rsp.status = scsi_cmd.status;	/* SCSI status */
467		if (iscsi_scsi_rsp_encap(rsp_header, &scsi_rsp) != 0) {
468			iscsi_err(__FILE__, __LINE__, "iscsi_scsi_rsp_encap() failed\n");
469			AHS_CLEANUP;
470			return -1;
471		}
472		if ((uint32_t)iscsi_sock_send_header_and_data(sess->sock, rsp_header, ISCSI_HEADER_LEN,
473		  scsi_cmd.send_data, scsi_rsp.length, scsi_cmd.send_sg_len)
474		    != ISCSI_HEADER_LEN + scsi_rsp.length) {
475			iscsi_err(__FILE__, __LINE__,
476				"iscsi_sock_send_header_and_data() failed\n");
477			AHS_CLEANUP;
478			return -1;
479		}
480		/* Make sure all data was transferred */
481
482		if (scsi_cmd.output) {
483#if 0
484			RETURN_NOT_EQUAL("scsi_cmd.bytes_recv", scsi_cmd.bytes_recv, scsi_cmd.trans_len, AHS_CLEANUP, -1);
485#else
486			if (scsi_cmd.bytes_recv != scsi_cmd.trans_len) {
487				iscsi_err(__FILE__, __LINE__,
488					"scsi_cmd.bytes_recv");
489				AHS_CLEANUP;
490				return -1;
491			}
492#endif
493			if (scsi_cmd.input) {
494#if 0
495				RETURN_NOT_EQUAL("scsi_cmd.bytes_sent", scsi_cmd.bytes_sent, scsi_cmd.bidi_trans_len, AHS_CLEANUP, -1);
496#else
497				if (scsi_cmd.bytes_sent !=
498						scsi_cmd.bidi_trans_len) {
499					iscsi_err(__FILE__, __LINE__,
500						"scsi_cmd.bytes_sent");
501					AHS_CLEANUP;
502					return -1;
503				}
504#endif
505			}
506		} else {
507			if (scsi_cmd.input) {
508#if 0
509				RETURN_NOT_EQUAL("scsi_cmd.bytes_sent", scsi_cmd.bytes_sent, scsi_cmd.trans_len, AHS_CLEANUP, -1);
510#else
511				if (scsi_cmd.bytes_sent != scsi_cmd.trans_len) {
512					iscsi_err(__FILE__, __LINE__,
513						"scsi_cmd.bytes_sent");
514					AHS_CLEANUP;
515					return -1;
516				}
517#endif
518			}
519		}
520	}
521
522	/* Device callback after command has completed */
523	if (cmd.callback) {
524		iscsi_trace(TRACE_ISCSI_DEBUG, "issuing device callback\n");
525		if ((*cmd.callback)(cmd.callback_arg) != 0) {
526			iscsi_err(__FILE__, __LINE__,
527				"device callback failed\n");
528			AHS_CLEANUP;
529			return -1;
530		}
531	}
532	AHS_CLEANUP;
533	return 0;
534}
535
536static int
537task_command_t(target_session_t * sess, uint8_t *header)
538{
539	iscsi_task_cmd_t cmd;
540	iscsi_task_rsp_t rsp;
541	uint8_t   rsp_header[ISCSI_HEADER_LEN];
542
543	/* Get & check args */
544
545	if (iscsi_task_cmd_decap(header, &cmd) != 0) {
546		iscsi_err(__FILE__, __LINE__,
547				"iscsi_task_cmd_decap() failed\n");
548		return -1;
549	}
550	if (cmd.CmdSN != sess->ExpCmdSN) {
551		iscsi_warn(__FILE__, __LINE__,
552			"Expected CmdSN %d, got %d. "
553			"(ignoring and resetting expectations)\n",
554			 cmd.CmdSN, sess->ExpCmdSN);
555		sess->ExpCmdSN = cmd.CmdSN;
556	}
557	sess->MaxCmdSN++;
558
559	(void) memset(&rsp, 0x0, sizeof(rsp));
560	rsp.response = ISCSI_TASK_RSP_FUNCTION_COMPLETE;
561
562	switch (cmd.function) {
563	case ISCSI_TASK_CMD_ABORT_TASK:
564		printf("ISCSI_TASK_CMD_ABORT_TASK\n");
565		break;
566	case ISCSI_TASK_CMD_ABORT_TASK_SET:
567		printf("ISCSI_TASK_CMD_ABORT_TASK_SET\n");
568		break;
569	case ISCSI_TASK_CMD_CLEAR_ACA:
570		printf("ISCSI_TASK_CMD_CLEAR_ACA\n");
571		break;
572	case ISCSI_TASK_CMD_CLEAR_TASK_SET:
573		printf("ISCSI_TASK_CMD_CLEAR_TASK_SET\n");
574		break;
575	case ISCSI_TASK_CMD_LOGICAL_UNIT_RESET:
576		printf("ISCSI_TASK_CMD_LOGICAL_UNIT_RESET\n");
577		break;
578	case ISCSI_TASK_CMD_TARGET_WARM_RESET:
579		printf("ISCSI_TASK_CMD_TARGET_WARM_RESET\n");
580		break;
581	case ISCSI_TASK_CMD_TARGET_COLD_RESET:
582		printf("ISCSI_TASK_CMD_TARGET_COLD_RESET\n");
583		break;
584	case ISCSI_TASK_CMD_TARGET_REASSIGN:
585		printf("ISCSI_TASK_CMD_TARGET_REASSIGN\n");
586		break;
587	default:
588		iscsi_err(__FILE__, __LINE__, "Unknown task function %d\n", cmd.function);
589		rsp.response = ISCSI_TASK_RSP_REJECTED;
590	}
591
592	rsp.tag = cmd.tag;
593	rsp.StatSN = ++(sess->StatSN);
594	rsp.ExpCmdSN = sess->ExpCmdSN;
595	rsp.MaxCmdSN = sess->MaxCmdSN;
596
597	if (iscsi_task_rsp_encap(rsp_header, &rsp) != 0) {
598		iscsi_err(__FILE__, __LINE__, "iscsi_task_cmd_decap() failed\n");
599		return -1;
600	}
601	if (iscsi_sock_msg(sess->sock, 1, ISCSI_HEADER_LEN, rsp_header, 0) != ISCSI_HEADER_LEN) {
602		iscsi_err(__FILE__, __LINE__, "iscsi_sock_msg() failed\n");
603		return -1;
604
605	}
606	return 0;
607}
608
609static int
610nop_out_t(target_session_t * sess, uint8_t *header)
611{
612	iscsi_nop_out_args_t nop_out;
613	char           *ping_data = NULL;
614
615	if (iscsi_nop_out_decap(header, &nop_out) != 0) {
616		iscsi_err(__FILE__, __LINE__, "iscsi_nop_out_decap() failed\n");
617		return -1;
618	}
619	if (nop_out.CmdSN != sess->ExpCmdSN) {
620		iscsi_warn(__FILE__, __LINE__, "Expected CmdSN %d, got %d. (ignoring and resetting expectations)\n",
621			      nop_out.CmdSN, sess->ExpCmdSN);
622		sess->ExpCmdSN = nop_out.CmdSN;
623	}
624	/* TODO Clarify whether we need to update the CmdSN */
625	/* sess->ExpCmdSN++;  */
626	/* sess->MaxCmdSN++;  */
627
628	if (nop_out.length) {
629		iscsi_trace(TRACE_ISCSI_DEBUG, "reading %u bytes ping data\n", nop_out.length);
630		if ((ping_data = iscsi_malloc(nop_out.length)) == NULL) {
631			iscsi_err(__FILE__, __LINE__, "iscsi_malloc() failed\n");
632			return -1;
633		}
634		if ((uint32_t)iscsi_sock_msg(sess->sock, 0, nop_out.length, ping_data, 0) != nop_out.length) {
635			iscsi_err(__FILE__, __LINE__, "iscsi_sock_msg() failed\n");
636			if (ping_data) {
637				iscsi_free(ping_data);
638			}
639			return -1;
640		}
641		iscsi_trace(TRACE_ISCSI_DEBUG, "successfully read %u bytes ping data:\n", nop_out.length);
642		iscsi_print_buffer(ping_data, nop_out.length);
643	}
644	if (nop_out.tag != 0xffffffff) {
645		iscsi_nop_in_args_t  nop_in;
646		uint8_t   rsp_header[ISCSI_HEADER_LEN];
647
648		iscsi_trace(TRACE_ISCSI_DEBUG, "sending %u bytes ping response\n", nop_out.length);
649		(void) memset(&nop_in, 0x0, sizeof(nop_in));
650		nop_in.length = nop_out.length;
651		nop_in.lun = nop_out.lun;
652		nop_in.tag = nop_out.tag;
653		nop_in.transfer_tag = 0xffffffff;
654		nop_in.StatSN = ++(sess->StatSN);
655		nop_in.ExpCmdSN = sess->ExpCmdSN;
656		nop_in.MaxCmdSN = sess->MaxCmdSN;
657
658		if (iscsi_nop_in_encap(rsp_header, &nop_in) != 0) {
659			iscsi_err(__FILE__, __LINE__, "iscsi_nop_in_encap() failed\n");
660			if (ping_data) {
661				iscsi_free(ping_data);
662			}
663			return -1;
664		}
665		if ((uint32_t)iscsi_sock_send_header_and_data(sess->sock, rsp_header, ISCSI_HEADER_LEN,
666						    ping_data, nop_in.length, 0) != ISCSI_HEADER_LEN + nop_in.length) {
667			iscsi_err(__FILE__, __LINE__, "iscsi_sock_send_header_and_data() failed\n");
668			if (ping_data) {
669				iscsi_free(ping_data);
670			}
671			return -1;
672		}
673		iscsi_trace(TRACE_ISCSI_DEBUG, "successfully sent %u bytes ping response\n", nop_out.length);
674	}
675	if (ping_data) {
676		iscsi_free(ping_data);
677	}
678	return 0;
679}
680
681/*
682 * text_command_t
683 */
684
685static int
686text_command_t(target_session_t * sess, uint8_t *header)
687{
688	iscsi_text_cmd_args_t	 text_cmd;
689	iscsi_text_rsp_args_t	 text_rsp;
690	unsigned		 len_in;
691	uint32_t		 i;
692	uint8_t   		 rsp_header[ISCSI_HEADER_LEN];
693	targv_t			*targv;
694	char           		*text_in = NULL;
695	char           		*text_out = NULL;
696	char			 buf[BUFSIZ];
697	int			 len_out = 0;
698
699#define TC_CLEANUP do {							\
700	if (text_in != NULL) {						\
701		iscsi_free_atomic(text_in);				\
702	}								\
703	if (text_out != NULL) {						\
704		iscsi_free_atomic(text_out);				\
705	}								\
706} while (/* CONSTCOND */ 0)
707#define TC_ERROR {							\
708	TC_CLEANUP;							\
709	return -1;							\
710}
711	/* Get text args */
712
713	if (iscsi_text_cmd_decap(header, &text_cmd) != 0) {
714		iscsi_err(__FILE__, __LINE__, "iscsi_text_cmd_decap() failed\n");
715		return -1;
716	}
717	/* Check args & update numbering */
718#if 0
719	RETURN_NOT_EQUAL("Continue", text_cmd.cont, 0, NO_CLEANUP, -1);
720	RETURN_NOT_EQUAL("CmdSN", text_cmd.CmdSN, sess->ExpCmdSN, NO_CLEANUP, -1);
721#else
722	if (text_cmd.cont != 0) {
723		iscsi_err(__FILE__, __LINE__, "Continue");
724		NO_CLEANUP;
725		return -1;
726	}
727	if (text_cmd.CmdSN != sess->ExpCmdSN) {
728		iscsi_err(__FILE__, __LINE__, "CmdSN");
729		NO_CLEANUP;
730		return -1;
731	}
732#endif
733
734	sess->ExpCmdSN++;
735	sess->MaxCmdSN++;
736
737	if ((text_out = iscsi_malloc_atomic(2048)) == NULL) {
738		iscsi_err(__FILE__, __LINE__,
739				"iscsi_malloc_atomic() failed\n");
740		return -1;
741	}
742
743	/* Read text parameters */
744	if ((len_in = text_cmd.length) != 0) {
745		iscsi_parameter_t *ptr;
746
747		if ((text_in = iscsi_malloc_atomic(len_in + 1)) == NULL) {
748			iscsi_err(__FILE__, __LINE__,
749				"iscsi_malloc_atomic() failed\n");
750			TC_CLEANUP;
751			return -1;
752		}
753		iscsi_trace(TRACE_ISCSI_DEBUG,
754			"reading %u bytes text parameters\n", len_in);
755		if ((unsigned)iscsi_sock_msg(sess->sock, 0, len_in, text_in,
756				0) != len_in) {
757			iscsi_err(__FILE__, __LINE__,
758				"iscsi_sock_msg() failed\n");
759			TC_CLEANUP;
760			return -1;
761		}
762		text_in[len_in] = 0x0;
763		PARAM_TEXT_PARSE(sess->params, &sess->sess_params.cred,
764			text_in, (int) len_in, text_out,
765			(int *)(void *)&len_out, 2048, 0, TC_ERROR);
766
767		/*
768		 * Handle exceptional cases not covered by parameters.c
769		 * (e.g., SendTargets)
770		 */
771		if ((ptr = param_get(sess->params, "SendTargets")) == NULL) {
772			iscsi_err(__FILE__, __LINE__,
773					"param_get() failed\n");
774			TC_CLEANUP;
775			return -1;
776		}
777		if (ptr->rx_offer) {
778			if (ptr->offer_rx &&
779			    strcmp(ptr->offer_rx, "All") == 0 &&
780			    !param_equiv(sess->params, "SessionType",
781			    	"Discovery")) {
782				iscsi_trace(TRACE_ISCSI_DEBUG,
783					"Rejecting SendTargets=All in a "
784					"non Discovery session\n");
785				PARAM_TEXT_ADD(sess->params, "SendTargets",
786					"Reject", text_out, &len_out, 2048,
787					0, TC_ERROR);
788			} else {
789				targv = sess->target->lunv;
790				for (i = 0 ; i < targv->c ; i++) {
791					if (sess->address_family == 6 ||
792					    (sess->address_family == 4 &&
793					    	allow_netmask(targv->v[i].mask,
794						sess->initiator))) {
795						(void) get_iqn(sess, i, buf,
796							sizeof(buf));
797						PARAM_TEXT_ADD(sess->params,
798							"TargetName", buf,
799							text_out, &len_out,
800							2048, 0, TC_ERROR);
801						PARAM_TEXT_ADD(sess->params,
802							"TargetAddress",
803			iscsi_target_getvar(sess->target, "target address"),
804							text_out, &len_out,
805							2048, 0, TC_ERROR);
806					} else {
807#ifdef HAVE_SYSLOG_H
808						syslog(LOG_INFO,
809							"WARNING: attempt to "
810							"discover targets from "
811							"%s (not allowed by %s)"
812							" has been rejected",
813							sess->initiator,
814							targv->v[0].mask);
815#endif
816					}
817				}
818			}
819			ptr->rx_offer = 0;
820		}
821		/* Parse outgoing offer */
822
823		if (len_out) {
824			PARAM_TEXT_PARSE(sess->params,
825				&sess->sess_params.cred, text_out, len_out,
826				NULL, NULL, 2048, 1, TC_ERROR);
827		}
828	}
829	if (sess->IsFullFeature) {
830		set_session_parameters(sess->params, &sess->sess_params);
831	}
832	/* Send response */
833
834	text_rsp.final = text_cmd.final;
835	text_rsp.cont = 0;
836	text_rsp.length = len_out;
837	text_rsp.lun = text_cmd.lun;
838	text_rsp.tag = text_cmd.tag;
839	text_rsp.transfer_tag = (text_rsp.final) ? 0xffffffff : 0x1234;
840	text_rsp.StatSN = ++(sess->StatSN);
841	text_rsp.ExpCmdSN = sess->ExpCmdSN;
842	text_rsp.MaxCmdSN = sess->MaxCmdSN;
843	if (iscsi_text_rsp_encap(rsp_header, &text_rsp) != 0) {
844		iscsi_err(__FILE__, __LINE__,
845			"iscsi_text_rsp_encap() failed\n");
846		TC_CLEANUP;
847		return -1;
848	}
849	if (iscsi_sock_msg(sess->sock, 1, ISCSI_HEADER_LEN, rsp_header, 0) !=
850			ISCSI_HEADER_LEN) {
851		iscsi_err(__FILE__, __LINE__,
852			"iscsi_sock_msg() failed\n");
853		TC_CLEANUP;
854		return -1;
855	}
856	if (len_out && iscsi_sock_msg(sess->sock, 1, (unsigned) len_out,
857			text_out, 0) != len_out) {
858		iscsi_err(__FILE__, __LINE__,
859			"iscsi_sock_msg() failed\n");
860		TC_CLEANUP;
861		return -1;
862	}
863	TC_CLEANUP;
864	return 0;
865}
866
867/* given a target's iqn, find the relevant target that we're exporting */
868int
869find_target_iqn(target_session_t *sess)
870{
871	uint32_t	 i;
872	targv_t		*targv;
873	char		 buf[BUFSIZ];
874
875	targv = sess->target->lunv;
876	for (i = 0 ; i < targv->c ; i++) {
877		if (param_equiv(sess->params, "TargetName",
878				get_iqn(sess, i, buf, sizeof(buf)))) {
879			return sess->d = i;
880		}
881	}
882	return -1;
883}
884
885/* given a tsih, find the relevant target that we're exporting */
886int
887find_target_tsih(iscsi_target_t *target, int tsih)
888{
889	uint32_t	 i;
890	targv_t		*targv;
891
892	targv = target->lunv;
893	for (i = 0 ; i < targv->c ; i++) {
894		if (targv->v[i].tsih == tsih) {
895			return i;
896		}
897	}
898	return -1;
899}
900
901/*
902 * login_command_t() handles login requests and replies.
903 */
904
905static int
906login_command_t(target_session_t * sess, uint8_t *header)
907{
908	iscsi_login_cmd_args_t cmd;
909	iscsi_login_rsp_args_t rsp;
910	uint8_t		rsp_header[ISCSI_HEADER_LEN];
911	targv_t		*targv;
912	char           *text_in = NULL;
913	char           *text_out = NULL;
914	char		logbuf[BUFSIZ];
915	int             len_in = 0;
916	int             len_out = 0;
917	int             status = 0;
918	int		i;
919
920	/* Initialize response */
921
922#define LC_CLEANUP do {							\
923	if (text_in != NULL) {						\
924		iscsi_free_atomic(text_in);				\
925	}								\
926	if (text_out != NULL) {						\
927		iscsi_free_atomic(text_out);				\
928	}								\
929} while (/* CONSTCOND */ 0)
930#define LC_ERROR {							\
931	TC_CLEANUP;							\
932	return -1;							\
933}
934
935	(void) memset(&rsp, 0x0, sizeof(rsp));
936	rsp.status_class = ISCSI_LOGIN_STATUS_INITIATOR_ERROR;
937
938	/* Get login args & check preconditions */
939
940	if (iscsi_login_cmd_decap(header, &cmd) != 0) {
941		iscsi_err(__FILE__, __LINE__,
942			"iscsi_login_cmd_decap() failed\n");
943		goto response;
944	}
945	if (sess->IsLoggedIn) {
946		iscsi_err(__FILE__, __LINE__,
947			"duplicate login attempt on sess %d\n", sess->id);
948		goto response;
949	}
950	if ((cmd.cont != 0) && (cmd.transit != 0)) {
951		iscsi_err(__FILE__, __LINE__,
952			"Bad cmd.continue.  Expected 0.\n");
953		goto response;
954	} else if ((cmd.version_max < ISCSI_VERSION) ||
955		   (cmd.version_min > ISCSI_VERSION)) {
956		iscsi_err(__FILE__, __LINE__,
957			"Target iscsi version (%u) not supported by initiator "
958			"[Max Ver (%u) and Min Ver (%u)]\n",
959			ISCSI_VERSION, cmd.version_max, cmd.version_min);
960		rsp.status_class = ISCSI_LOGIN_STATUS_INITIATOR_ERROR;
961		rsp.status_detail = ISCSI_LOGIN_DETAIL_VERSION_NOT_SUPPORTED;
962		rsp.version_max = ISCSI_VERSION;
963		rsp.version_active = ISCSI_VERSION;
964		goto response;
965	} else if (cmd.tsih != 0) {
966		iscsi_err(__FILE__, __LINE__,
967			"Bad cmd.tsih (%u). Expected 0.\n", cmd.tsih);
968		goto response;
969	}
970
971	/* Parse text parameters and build response */
972	if ((text_out = iscsi_malloc_atomic(2048)) == NULL) {
973		iscsi_err(__FILE__, __LINE__,
974			"iscsi_malloc_atomic() failed\n");
975		return -1;
976	}
977	if ((len_in = cmd.length) != 0) {
978		iscsi_trace(TRACE_ISCSI_DEBUG,
979			"reading %d bytes text data\n", len_in);
980		text_in = iscsi_malloc_atomic((unsigned)(len_in + 1));
981		if (text_in == NULL) {
982			iscsi_err(__FILE__, __LINE__,
983				"iscsi_malloc() failed\n");
984			LC_CLEANUP;
985			return -1;
986		}
987		if (iscsi_sock_msg(sess->sock, 0, (unsigned) len_in, text_in,
988				0) != len_in) {
989			iscsi_err(__FILE__, __LINE__,
990				"iscsi_sock_msg() failed\n");
991			LC_CLEANUP;
992			return -1;
993		}
994		text_in[len_in] = 0x0;
995		iscsi_trace(TRACE_ISCSI_DEBUG,
996			"successfully read %d bytes text data\n", len_in);
997
998		/*
999		 * Parse incoming parameters (text_out will contain the
1000		 * response we need
1001		 */
1002
1003		/* to send back to the initiator */
1004
1005
1006		status = param_text_parse(sess->params,
1007				&sess->sess_params.cred, text_in, len_in,
1008				text_out, &len_out, 2048, 0);
1009		if (status != 0) {
1010			switch (status) {
1011			case ISCSI_PARAM_STATUS_FAILED:
1012				rsp.status_detail = ISCSI_LOGIN_DETAIL_SUCCESS;
1013				break;
1014			case ISCSI_PARAM_STATUS_AUTH_FAILED:
1015				rsp.status_detail =
1016					ISCSI_LOGIN_DETAIL_INIT_AUTH_FAILURE;
1017				break;
1018			default:
1019				/*
1020				 * We will need to set the detail
1021				 * field based on more detailed error
1022				 * cases.  Will need to fix this if
1023				 * compliciance test break
1024		                 * (status_detail field).
1025		                 */
1026				break;
1027			}
1028			goto response;
1029		}
1030		/* Parse the outgoing offer */
1031		if (!sess->LoginStarted) {
1032			PARAM_TEXT_ADD(sess->params, "TargetPortalGroupTag",
1033				"1", text_out, &len_out, 2048, 0, LC_ERROR);
1034		}
1035		if (len_out) {
1036			PARAM_TEXT_PARSE(sess->params,
1037				&sess->sess_params.cred, text_out, len_out,
1038				NULL, NULL, 2048, 1, LC_ERROR;
1039			);
1040		}
1041	}
1042	if (!sess->LoginStarted) {
1043		sess->LoginStarted = 1;
1044	}
1045	/*
1046	 * For now, we accept what ever the initiators' current and next
1047	 * states are. And le are always
1048	 */
1049	/* ready to transitition to that state. */
1050
1051	rsp.csg = cmd.csg;
1052	rsp.nsg = cmd.nsg;
1053	rsp.transit = cmd.transit;
1054
1055	if (cmd.csg == ISCSI_LOGIN_STAGE_SECURITY) {
1056		if (param_equiv(sess->params, "AuthResult", "No")) {
1057			rsp.transit = 0;
1058		} else if (param_equiv(sess->params, "AuthResult", "Fail")) {
1059			rsp.status_class = rsp.status_detail =
1060				ISCSI_LOGIN_DETAIL_INIT_AUTH_FAILURE;
1061			goto response;
1062		}
1063	}
1064	if (cmd.transit && cmd.nsg == ISCSI_LOGIN_STAGE_FULL_FEATURE) {
1065		iscsi_trace(TRACE_ISCSI_DEBUG,
1066			"transitioning to ISCSI_LOGIN_STAGE_FULL_FEATURE\n");
1067
1068		/* Check post conditions */
1069		if (param_equiv(sess->params, "InitiatorName", "")) {
1070			iscsi_err(__FILE__, __LINE__,
1071				"InitiatorName not specified\n");
1072			goto response;
1073		}
1074		if (param_equiv(sess->params, "SessionType", "Normal")) {
1075			if (param_equiv(sess->params, "TargetName", "")) {
1076				iscsi_err(__FILE__, __LINE__,
1077					"TargetName not specified\n");
1078				goto response;
1079			}
1080			if ((i = find_target_iqn(sess)) < 0) {
1081				iscsi_err(__FILE__, __LINE__,
1082					"Bad TargetName \"%s\"\n",
1083					param_val(sess->params, "TargetName"));
1084				goto response;
1085			}
1086			if (cmd.tsih != 0 &&
1087			    find_target_tsih(sess->target, cmd.tsih) != i) {
1088				targv = sess->target->lunv;
1089				iscsi_err(__FILE__, __LINE__,
1090					"target tsih expected %d, cmd.tsih %d, "
1091					"i %d\n", targv->v[i].tsih, cmd.tsih,
1092					i);
1093			}
1094			sess->d = i;
1095		} else if ((i = find_target_tsih(sess->target, cmd.tsih)) < 0) {
1096			iscsi_err(__FILE__, __LINE__,
1097				"Abnormal SessionType cmd.tsih %d not found\n",
1098				cmd.tsih);
1099			i = sess->d;
1100		}
1101		if (param_equiv(sess->params, "SessionType", "")) {
1102			iscsi_err(__FILE__, __LINE__,
1103				"SessionType not specified\n");
1104			goto response;
1105		}
1106		sess->ExpCmdSN = sess->MaxCmdSN = cmd.CmdSN;
1107		sess->cid = cmd.cid;
1108		sess->isid = cmd.isid;
1109
1110		targv = sess->target->lunv;
1111		targv->v[i].tsih = sess->tsih = ++sess->target->last_tsih;
1112		sess->IsFullFeature = 1;
1113
1114		sess->IsLoggedIn = 1;
1115		if (!param_equiv(sess->params, "SessionType", "Discovery")) {
1116			(void) strlcpy(param_val(sess->params,
1117					"MaxConnections"), "1", 2);
1118		}
1119		set_session_parameters(sess->params, &sess->sess_params);
1120	} else {
1121		if ((i = find_target_tsih(sess->target, cmd.tsih)) < 0) {
1122			iscsi_err(__FILE__, __LINE__,
1123				"cmd.tsih %d not found\n", cmd.tsih);
1124		}
1125	}
1126
1127	/* No errors */
1128	rsp.status_class = rsp.status_detail = ISCSI_LOGIN_DETAIL_SUCCESS;
1129	rsp.length = len_out;
1130
1131	/* Send login response */
1132response:
1133	sess->ExpCmdSN = sess->MaxCmdSN = cmd.CmdSN;
1134	rsp.isid = cmd.isid;
1135	rsp.StatSN = cmd.ExpStatSN;	/* debug  */
1136	rsp.tag = cmd.tag;
1137	rsp.cont = cmd.cont;
1138	rsp.ExpCmdSN = sess->ExpCmdSN;
1139	rsp.MaxCmdSN = sess->MaxCmdSN;
1140	if (!rsp.status_class) {
1141		if (rsp.transit &&
1142		    (rsp.nsg == ISCSI_LOGIN_STAGE_FULL_FEATURE)) {
1143			rsp.version_max = ISCSI_VERSION;
1144			rsp.version_active = ISCSI_VERSION;
1145			rsp.StatSN = ++(sess->StatSN);
1146			rsp.tsih = sess->tsih;
1147		}
1148	}
1149	if (iscsi_login_rsp_encap(rsp_header, &rsp) != 0) {
1150		iscsi_err(__FILE__, __LINE__,
1151			"iscsi_login_rsp_encap() failed\n");
1152		LC_CLEANUP;
1153		return -1;
1154	}
1155	iscsi_trace(TRACE_ISCSI_DEBUG, "sending login response\n");
1156	if ((uint32_t)iscsi_sock_send_header_and_data(sess->sock, rsp_header,
1157			ISCSI_HEADER_LEN, text_out, rsp.length, 0) !=
1158			ISCSI_HEADER_LEN + rsp.length) {
1159		iscsi_err(__FILE__, __LINE__,
1160			"iscsi_sock_send_header_and_data() failed\n");
1161		LC_CLEANUP;
1162		return -1;
1163	}
1164	iscsi_trace(TRACE_ISCSI_DEBUG,
1165		"sent login response ok\n");
1166	if (rsp.status_class != 0) {
1167		LC_CLEANUP;
1168		return -1;
1169	}
1170	if (cmd.transit && cmd.nsg == ISCSI_LOGIN_STAGE_FULL_FEATURE) {
1171
1172		/* log information to stdout */
1173		(void) snprintf(logbuf, sizeof(logbuf),
1174			"> iSCSI %s login  successful from %s on %s disk %d, "
1175			"ISID %" PRIu64 ", TSIH %u",
1176			param_val(sess->params, "SessionType"),
1177			param_val(sess->params, "InitiatorName"),
1178			sess->initiator,
1179			sess->d,
1180			sess->isid,
1181			sess->tsih);
1182		printf("%s\n", logbuf);
1183#ifdef HAVE_SYSLOG_H
1184		/* log information to syslog */
1185		syslog(LOG_INFO, "%s", logbuf);
1186#endif
1187
1188		/* Buffer for data xfers to/from the scsi device */
1189		if (!param_equiv(sess->params, "MaxRecvDataSegmentLength",
1190					"0")) {
1191			sess->buff = iscsi_malloc((unsigned)(
1192					param_atoi(sess->params,
1193					"MaxRecvDataSegmentLength")));
1194			if (sess->buff == NULL) {
1195				iscsi_err(__FILE__, __LINE__,
1196					"iscsi_malloc() failed\n");
1197				LC_CLEANUP;
1198				return -1;
1199			}
1200		} else {
1201			iscsi_err(__FILE__, __LINE__,
1202				"0 MaxRecvDataSegmentLength not supported\n");
1203			LC_CLEANUP;
1204			return -1;
1205		}
1206	}
1207	LC_CLEANUP;
1208	return 0;
1209}
1210
1211static int
1212logout_command_t(target_session_t * sess, uint8_t *header)
1213{
1214	iscsi_logout_cmd_args_t	cmd;
1215	iscsi_logout_rsp_args_t	rsp;
1216	targv_t			*targv;
1217	uint8_t			rsp_header[ISCSI_HEADER_LEN];
1218	char			logbuf[BUFSIZ];
1219	int			i;
1220
1221	(void) memset(&rsp, 0x0, sizeof(rsp));
1222	if (iscsi_logout_cmd_decap(header, &cmd) != 0) {
1223		iscsi_err(__FILE__, __LINE__,
1224			"iscsi_logout_cmd_decap() failed\n");
1225		return -1;
1226	}
1227	sess->StatSN = cmd.ExpStatSN;
1228	if ((cmd.reason == ISCSI_LOGOUT_CLOSE_RECOVERY) &&
1229	    (param_equiv(sess->params, "ErrorRecoveryLevel", "0"))) {
1230		rsp.response = ISCSI_LOGOUT_STATUS_NO_RECOVERY;
1231	}
1232#if 0
1233	RETURN_NOT_EQUAL("CmdSN", cmd.CmdSN, sess->ExpCmdSN, NO_CLEANUP, -1);
1234	RETURN_NOT_EQUAL("ExpStatSN", cmd.ExpStatSN, sess->StatSN, NO_CLEANUP, -1);
1235#else
1236	if (cmd.CmdSN != sess->ExpCmdSN) {
1237		iscsi_err(__FILE__, __LINE__, "CmdSN");
1238		NO_CLEANUP;
1239		return -1;
1240	}
1241	if (cmd.ExpStatSN != sess->StatSN) {
1242		iscsi_err(__FILE__, __LINE__, "ExpStatSN");
1243		NO_CLEANUP;
1244		return -1;
1245	}
1246#endif
1247
1248	rsp.tag = cmd.tag;
1249	rsp.StatSN = sess->StatSN;
1250	rsp.ExpCmdSN = ++sess->ExpCmdSN;
1251	rsp.MaxCmdSN = sess->MaxCmdSN;
1252	if (iscsi_logout_rsp_encap(rsp_header, &rsp) != 0) {
1253		iscsi_err(__FILE__, __LINE__,
1254				"iscsi_logout_rsp_encap() failed\n");
1255		return -1;
1256	}
1257	if (iscsi_sock_msg(sess->sock, 1, ISCSI_HEADER_LEN, rsp_header, 0) !=
1258			ISCSI_HEADER_LEN) {
1259		iscsi_err(__FILE__, __LINE__,
1260			"iscsi_sock_msg() failed\n");
1261		return -1;
1262	}
1263	iscsi_trace(TRACE_ISCSI_DEBUG, "sent logout response OK\n");
1264
1265	/* log information to stdout */
1266	(void) snprintf(logbuf, sizeof(logbuf),
1267		"< iSCSI %s logout successful from %s on %s "
1268		"disk %d, ISID %" PRIu64 ", TSIH %u",
1269		param_val(sess->params, "SessionType"),
1270		param_val(sess->params, "InitiatorName"),
1271		sess->initiator,
1272		sess->d,
1273		sess->isid,
1274		sess->tsih);
1275	printf("%s\n", logbuf);
1276#ifdef HAVE_SYSLOG
1277	/* log information to syslog */
1278	syslog(LOG_INFO, "%s", logbuf);
1279#endif
1280
1281	sess->IsLoggedIn = 0;
1282
1283	if (sess->sess_params.cred.user) {
1284		free(sess->sess_params.cred.user);
1285		sess->sess_params.cred.user = NULL;
1286	}
1287
1288	if ((i = find_target_tsih(sess->target, sess->tsih)) < 0) {
1289		iscsi_err(__FILE__, __LINE__,
1290			"logout sess->tsih %d not found\n", sess->tsih);
1291	} else {
1292		targv = sess->target->lunv;
1293		targv->v[i].tsih = 0;
1294	}
1295	sess->tsih = 0;
1296
1297	return 0;
1298}
1299
1300static int
1301verify_cmd_t(target_session_t * sess, uint8_t *header)
1302{
1303	int             op = ISCSI_OPCODE(header);
1304
1305	if ((!sess->LoginStarted) && (op != ISCSI_LOGIN_CMD)) {
1306		/* Terminate the connection */
1307		iscsi_err(__FILE__, __LINE__,
1308			"session %d: iSCSI op %#x attempted "
1309			"before LOGIN PHASE\n",
1310			    sess->id, op);
1311		return -1;
1312	}
1313	if (!sess->IsFullFeature &&
1314	    ((op != ISCSI_LOGIN_CMD) && (op != ISCSI_LOGOUT_CMD))) {
1315		iscsi_login_rsp_args_t rsp;
1316		uint8_t   rsp_header[ISCSI_HEADER_LEN];
1317		iscsi_err(__FILE__, __LINE__,
1318			"session %d: iSCSI op %#x before FULL FEATURE\n",
1319			sess->id, op);
1320		/* Create Login Reject response */
1321		(void) memset(&rsp, 0x0, sizeof(rsp));
1322		rsp.status_class = ISCSI_LOGIN_STATUS_INITIATOR_ERROR;
1323		rsp.status_detail = ISCSI_LOGIN_DETAIL_NOT_LOGGED_IN;
1324		rsp.version_max = ISCSI_VERSION;
1325		rsp.version_active = ISCSI_VERSION;
1326
1327		if (iscsi_login_rsp_encap(rsp_header, &rsp) != 0) {
1328			iscsi_err(__FILE__, __LINE__,
1329				"iscsi_login_rsp_encap() failed\n");
1330			return -1;
1331		}
1332		iscsi_trace(TRACE_ISCSI_DEBUG, "sending login response\n");
1333		if ((uint32_t)iscsi_sock_send_header_and_data(sess->sock,
1334				rsp_header, ISCSI_HEADER_LEN, NULL, 0, 0) !=
1335				ISCSI_HEADER_LEN + rsp.length) {
1336			iscsi_err(__FILE__, __LINE__,
1337				"iscsi_sock_send_header_and_data() failed\n");
1338			return -1;
1339		}
1340		iscsi_trace(TRACE_ISCSI_DEBUG, "sent login response ok\n");
1341		return -1;
1342	}
1343	return 0;
1344}
1345
1346/*
1347 * this function looks at the opcode in the received header for the session,
1348 * and does a switch on the opcode to call the required function.
1349 */
1350static int
1351execute_t(target_session_t *sess, uint8_t *header)
1352{
1353	int             op = ISCSI_OPCODE(header);
1354
1355	if (verify_cmd_t(sess, header) != 0) {
1356		return -1;
1357	}
1358	switch (op) {
1359	case ISCSI_TASK_CMD:
1360		iscsi_trace(TRACE_ISCSI_CMD,
1361				"session %d: Task Command\n", sess->id);
1362		if (task_command_t(sess, header) != 0) {
1363			iscsi_err(__FILE__, __LINE__,
1364				"task_command_t() failed\n");
1365			return -1;
1366		}
1367		break;
1368
1369	case ISCSI_NOP_OUT:
1370		iscsi_trace(TRACE_ISCSI_CMD, "session %d: NOP-Out\n", sess->id);
1371		if (nop_out_t(sess, header) != 0) {
1372			iscsi_err(__FILE__, __LINE__,
1373				"nop_out_t() failed\n");
1374			return -1;
1375		}
1376		break;
1377
1378	case ISCSI_LOGIN_CMD:
1379		iscsi_trace(TRACE_ISCSI_CMD,
1380				"session %d: Login Command\n", sess->id);
1381		if (login_command_t(sess, header) != 0) {
1382			iscsi_err(__FILE__, __LINE__,
1383				"login_command_t() failed\n");
1384			return -1;
1385		}
1386		break;
1387
1388	case ISCSI_TEXT_CMD:
1389		iscsi_trace(TRACE_ISCSI_CMD,
1390				"session %d: Text Command\n", sess->id);
1391		if (text_command_t(sess, header) != 0) {
1392			iscsi_err(__FILE__, __LINE__,
1393				"text_command_t() failed\n");
1394			return -1;
1395		}
1396		break;
1397
1398	case ISCSI_LOGOUT_CMD:
1399		iscsi_trace(TRACE_ISCSI_CMD,
1400			"session %d: Logout Command\n", sess->id);
1401		if (logout_command_t(sess, header) != 0) {
1402			iscsi_err(__FILE__, __LINE__,
1403				"logout_command_t() failed\n");
1404			return -1;
1405		}
1406		break;
1407
1408	case ISCSI_SCSI_CMD:
1409		iscsi_trace(TRACE_ISCSI_CMD,
1410			"session %d: SCSI Command\n", sess->id);
1411		if (scsi_command_t(sess, header) != 0) {
1412			iscsi_err(__FILE__, __LINE__,
1413				"scsi_command_t() failed\n");
1414			return -1;
1415		}
1416		break;
1417
1418	default:
1419		iscsi_err(__FILE__, __LINE__, "Unknown Opcode %#x\n",
1420			ISCSI_OPCODE(header));
1421		if (reject_t(sess, header, 0x04) != 0) {
1422			iscsi_err(__FILE__, __LINE__,
1423				"reject_t() failed\n");
1424			return -1;
1425		}
1426		break;
1427	}
1428	return 0;
1429}
1430
1431/*
1432 * Currently one thread per session, used for both Rx and Tx.
1433 */
1434static int
1435worker_proc_t(void *arg)
1436{
1437	target_session_t *sess = (target_session_t *) arg;
1438	uint8_t   header[ISCSI_HEADER_LEN];
1439	iscsi_parameter_t **l = &sess->params;
1440
1441	ISCSI_THREAD_START("worker_thread");
1442	sess->worker.pid = getpid();
1443	sess->worker.state |= ISCSI_WORKER_STATE_STARTED;
1444	iscsi_trace(TRACE_ISCSI_DEBUG, "session %d: started\n", sess->id);
1445
1446	/*
1447         * ISCSI_PARAM_TYPE_LIST format:        <type> <key> <dflt> <valid list values>
1448         * ISCSI_PARAM_TYPE_BINARY format:      <type> <key> <dflt> <valid binary values>
1449         * ISCSI_PARAM_TYPE_NUMERICAL format:   <type> <key> <dflt> <max>
1450         * ISCSI_PARAM_TYPE_DECLARATIVE format: <type> <key> <dflt> ""
1451         */
1452
1453	sess->params = NULL;
1454	l = &sess->params;
1455
1456	/* CHAP Parameters */
1457	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_LIST, "AuthMethod", "CHAP", "CHAP,None", return -1);
1458	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_LIST, "CHAP_A", "None", "5", return -1);
1459	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_DECLARATIVE, "CHAP_N", "", "", return -1);
1460	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_DECLARATIVE, "CHAP_R", "", "", return -1);
1461	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_DECLARATIVE, "CHAP_I", "", "", return -1);
1462	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_DECLARATIVE, "CHAP_C", "", "", return -1);
1463	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_DECLARATIVE, "TargetPortalGroupTag", "1", "1", return -1);
1464	/* CHAP Parameters */
1465	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_LIST, "HeaderDigest", "None", "None", return -1);
1466	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_LIST, "DataDigest", "None", "None", return -1);
1467	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_NUMERICAL, "MaxConnections", "1", "1", return -1);
1468	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_DECLARATIVE, "SendTargets", "", "", return -1);
1469	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_DECLARATIVE, "TargetName", "", "", return -1);
1470	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_DECLARATIVE, "InitiatorName", "", "", return -1);
1471	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_DECLARATIVE, "TargetAlias", "", "", return -1);
1472	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_DECLARATIVE, "InitiatorAlias", "", "", return -1);
1473	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_DECLARATIVE, "TargetAddress", "", "", return -1);
1474	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_BINARY_OR, "InitialR2T", "Yes", "Yes,No", return -1);
1475	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_BINARY_AND, "OFMarker", "No", "Yes,No", return -1);
1476	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_BINARY_AND, "IFMarker", "No", "Yes,No", return -1);
1477	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_NUMERICAL_Z, "OFMarkInt", "1", "65536", return -1);
1478	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_NUMERICAL_Z, "IFMarkInt", "1", "65536", return -1);
1479	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_BINARY_AND, "ImmediateData", "Yes", "Yes,No", return -1);
1480	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_NUMERICAL_Z, "MaxRecvDataSegmentLength", "8192", "16777215", return -1);
1481	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_NUMERICAL_Z, "MaxBurstLength", "262144", "16777215", return -1);
1482	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_NUMERICAL_Z, "FirstBurstLength", "65536", "16777215", return -1);
1483	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_NUMERICAL, "DefaultTime2Wait", "2", "2", return -1);
1484	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_NUMERICAL, "DefaultTime2Retain", "20", "20", return -1);
1485	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_NUMERICAL, "MaxOutstandingR2T", "1", "1", return -1);
1486	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_BINARY_OR, "DataPDUInOrder", "Yes", "Yes,No", return -1);
1487	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_BINARY_OR, "DataSequenceInOrder", "Yes", "Yes,No", return -1);
1488	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_NUMERICAL, "ErrorRecoveryLevel", "0", "0", return -1);
1489	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_DECLARATIVE, "SessionType", "Normal", "Normal,Discovery", return -1);
1490	/*
1491	 * Auth Result is not in specs, we use this key to pass
1492	 * authentication result
1493	 */
1494	PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_LIST, "AuthResult", "No", "Yes,No,Fail", return -1);
1495
1496	/* Set remaining session parameters  */
1497
1498	sess->UsePhaseCollapsedRead = ISCSI_USE_PHASE_COLLAPSED_READ_DFLT;
1499
1500	/* Loop for commands */
1501
1502	while (sess->target->state != TARGET_SHUT_DOWN) {
1503		iscsi_trace(TRACE_ISCSI_DEBUG,
1504			"session %d: reading header\n", sess->id);
1505		if (iscsi_sock_msg(sess->sock, 0, ISCSI_HEADER_LEN, header, 0)
1506				!= ISCSI_HEADER_LEN) {
1507			iscsi_trace(TRACE_ISCSI_DEBUG,
1508				"session %d: iscsi_sock_msg() failed\n",
1509				sess->id);
1510			break;
1511		}
1512		iscsi_trace(TRACE_ISCSI_DEBUG,
1513			"session %d: iscsi op %#x\n", sess->id,
1514			ISCSI_OPCODE(header));
1515		if (execute_t(sess, header) != 0) {
1516			iscsi_err(__FILE__, __LINE__,
1517				"execute_t() failed\n");
1518			break;
1519		}
1520		iscsi_trace(TRACE_ISCSI_DEBUG,
1521			"session %d: iscsi op %#x complete\n", sess->id,
1522			ISCSI_OPCODE(header));
1523		if (ISCSI_OPCODE(header) == ISCSI_LOGOUT_CMD) {
1524			iscsi_trace(TRACE_ISCSI_DEBUG,
1525				"session %d: logout received, ending session\n",
1526				sess->id);
1527			break;
1528		}
1529	}
1530
1531	/* Clean up */
1532
1533	iscsi_free(sess->buff);
1534	if (param_list_destroy(sess->params) != 0) {
1535		iscsi_err(__FILE__, __LINE__,
1536			"param_list_destroy() failed\n");
1537		return -1;
1538	}
1539	/* Terminate connection */
1540
1541	if (iscsi_sock_close(sess->sock) != 0) {
1542		iscsi_err(__FILE__, __LINE__,
1543			"iscsi_sock_close() failed\n");
1544	}
1545	/* Make session available */
1546
1547	ISCSI_LOCK(&g_session_q_mutex, return -1);
1548	(void) memset(sess, 0x0, sizeof(*sess));
1549	sess->d = -1;
1550	if (iscsi_queue_insert(&g_session_q, sess) != 0) {
1551		iscsi_err(__FILE__, __LINE__,
1552				"iscsi_queue_insert() failed\n");
1553		return -1;
1554	}
1555	ISCSI_UNLOCK(&g_session_q_mutex, return -1);
1556	iscsi_trace(TRACE_ISCSI_DEBUG, "session %d: ended\n", sess->id);
1557
1558	return 0;
1559}
1560
1561static int
1562read_data_pdu(target_session_t * sess,
1563	      iscsi_write_data_t * data,
1564	      iscsi_scsi_cmd_args_t * args)
1565{
1566	uint8_t   header[ISCSI_HEADER_LEN];
1567	int             ret_val = -1;
1568
1569	if (iscsi_sock_msg(sess->sock, 0, ISCSI_HEADER_LEN, header, 0) !=
1570			ISCSI_HEADER_LEN) {
1571		iscsi_err(__FILE__, __LINE__,
1572			"iscsi_sock_msg() failed\n");
1573		return -1;
1574	}
1575	if ((ret_val = iscsi_write_data_decap(header, data)) != 0) {
1576		iscsi_err(__FILE__, __LINE__,
1577			"iscsi_write_data_decap() failed\n");
1578		return ret_val;
1579	}
1580	/* Check args */
1581	if (sess->sess_params.max_dataseg_len) {
1582		if (data->length > sess->sess_params.max_dataseg_len) {
1583			args->status = 0x02;
1584			return -1;
1585		}
1586	}
1587	if ((args->bytes_recv + data->length) > args->trans_len) {
1588		args->status = 0x02;
1589		return -1;
1590	}
1591	if (data->tag != args->tag) {
1592		iscsi_trace(TRACE_ISCSI_DEBUG,
1593			"Data ITT (%d) does not match with command ITT (%d)\n",
1594			data->tag, args->tag);
1595		if (data->final) {
1596			args->status = 0x02;
1597			return -1;
1598		} else {
1599			/* Send a reject PDU */
1600			iscsi_trace(TRACE_ISCSI_DEBUG, "Sending Reject PDU\n");
1601			if (reject_t(sess, header, 0x09) != 0) {
1602				/* Invalid PDU Field */
1603				iscsi_trace(TRACE_ISCSI_DEBUG,
1604					"Sending Reject PDU failed\n");
1605				return 1;
1606			}
1607		}
1608	}
1609	return 0;
1610}
1611
1612int
1613target_transfer_data(target_session_t * sess, iscsi_scsi_cmd_args_t * args,
1614		struct iovec * sg, int sg_len)
1615{
1616	iscsi_write_data_t data;
1617	struct iovec   *iov, *iov_ptr = NULL;
1618	int             iov_len;
1619
1620#define TTD_CLEANUP do {						\
1621	if (iov_ptr != NULL) {						\
1622		iscsi_free_atomic(iov_ptr);				\
1623	}								\
1624} while (/* CONSTCOND */ 0)
1625
1626	args->bytes_recv = 0;
1627	if ((!sess->sess_params.immediate_data) && args->length) {
1628		iscsi_trace(TRACE_ISCSI_DEBUG,
1629			"Cannot accept any Immediate data\n");
1630		args->status = 0x02;
1631		return -1;
1632	}
1633
1634	/* Make a copy of the iovec */
1635	iov_ptr = iscsi_malloc_atomic(sizeof(struct iovec) * sg_len);
1636	if (iov_ptr == NULL) {
1637		iscsi_err(__FILE__, __LINE__,
1638				"iscsi_malloc_atomic() failed\n");
1639		return -1;
1640	}
1641	iov = iov_ptr;
1642	(void) memcpy(iov, sg, sizeof(struct iovec) * sg_len);
1643	iov_len = sg_len;
1644
1645	/*
1646         * Read any immediate data.
1647         */
1648
1649	if (sess->sess_params.immediate_data && args->length) {
1650		if (sess->sess_params.max_dataseg_len &&
1651		    args->length > sess->sess_params.max_dataseg_len) {
1652			iscsi_err(__FILE__, __LINE__,
1653				"args->length (%u) too long\n",
1654				args->length);
1655				TTD_CLEANUP;
1656				return -1;
1657		}
1658
1659		/* Modify iov to include just immediate data */
1660		if (modify_iov(&iov, &iov_len, 0, args->length) != 0) {
1661			iscsi_err(__FILE__, __LINE__,
1662				"modify_iov() failed\n");
1663			TTD_CLEANUP;
1664			return -1;
1665		}
1666		iscsi_trace(TRACE_SCSI_DATA,
1667			"reading %u bytes immediate write data\n",
1668			args->length);
1669		if ((uint32_t)iscsi_sock_msg(sess->sock, 0, args->length, iov,
1670				iov_len) != args->length) {
1671			iscsi_err(__FILE__, __LINE__,
1672				"iscsi_sock_msg() failed\n");
1673			TTD_CLEANUP;
1674			return -1;
1675		}
1676		iscsi_trace(TRACE_SCSI_DATA,
1677			"successfully read %u bytes immediate write data\n",
1678			args->length);
1679		args->bytes_recv += args->length;
1680	}
1681
1682	/*
1683         * Read iSCSI data PDUs
1684         */
1685	if (args->bytes_recv < args->trans_len) {
1686		int             r2t_flag = 0;
1687		int             read_status = 0;
1688		iscsi_r2t_t     r2t;
1689		int             desired_xfer_len;
1690
1691		desired_xfer_len = MIN(sess->sess_params.first_burst_length,
1692				      args->trans_len) - args->bytes_recv;
1693		(void) memset(&r2t, 0x0, sizeof(r2t));
1694		do {
1695
1696			/*
1697			 * Send R2T if we're either operating in solicted
1698			 * mode or we're operating in unsolicted
1699			 */
1700			/* mode and have reached the first burst */
1701			if (!r2t_flag &&
1702			     (sess->sess_params.initial_r2t ||
1703			        (sess->sess_params.first_burst_length &&
1704				(args->bytes_recv >=
1705				sess->sess_params.first_burst_length)))) {
1706				uint8_t   header[ISCSI_HEADER_LEN];
1707
1708				desired_xfer_len = MIN(args->trans_len -
1709							args->bytes_recv,
1710					sess->sess_params.max_burst_length);
1711				iscsi_trace(TRACE_ISCSI_DEBUG,
1712					"sending R2T for %u bytes data\n",
1713					desired_xfer_len);
1714				r2t.tag = args->tag;
1715
1716				r2t.transfer_tag = 0x1234;
1717
1718				r2t.ExpCmdSN = sess->ExpCmdSN;
1719				r2t.MaxCmdSN = sess->MaxCmdSN;
1720				r2t.StatSN = ++(sess->StatSN);
1721				r2t.length = desired_xfer_len;
1722				r2t.offset = args->bytes_recv;
1723				if (iscsi_r2t_encap(header, &r2t) != 0) {
1724					iscsi_err(__FILE__, __LINE__,
1725						"r2t_encap() failed\n");
1726					TTD_CLEANUP;
1727					return -1;
1728				}
1729				iscsi_trace(TRACE_ISCSI_DEBUG,
1730					"sending R2T tag %u transfer tag "
1731					"%u len %u offset %u\n",
1732					r2t.tag, r2t.transfer_tag, r2t.length,
1733					r2t.offset);
1734				if (iscsi_sock_msg(sess->sock, 1,
1735					ISCSI_HEADER_LEN, header, 0) !=
1736					ISCSI_HEADER_LEN) {
1737					iscsi_err(__FILE__, __LINE__,
1738						"iscsi_sock_msg() failed\n");
1739					TTD_CLEANUP;
1740					return -1;
1741				}
1742				r2t_flag = 1;
1743				r2t.R2TSN += 1;
1744			}
1745
1746			/* Read iSCSI data PDU */
1747			iscsi_trace(TRACE_ISCSI_DEBUG, "reading data pdu\n");
1748			read_status = read_data_pdu(sess, &data, args);
1749			if (read_status != 0) {
1750				if (read_status == 1) {
1751					iscsi_trace(TRACE_ISCSI_DEBUG,
1752						"Unknown PDU received and "
1753						"ignored. Expecting "
1754						"Data PDU\n");
1755					continue;
1756				} else {
1757					iscsi_err(__FILE__, __LINE__,
1758						"read_data_pdu() failed\n");
1759					args->status = 0x02;
1760					TTD_CLEANUP;
1761					return -1;
1762				}
1763			}
1764			if (data.ExpStatSN != sess->StatSN) {
1765				iscsi_warn(__FILE__, __LINE__,
1766					"Bad \"ExpStatSN\": Got %u "
1767					"expected %u.\n",
1768					data.ExpStatSN, sess->StatSN);
1769			}
1770			iscsi_trace(TRACE_ISCSI_DEBUG,
1771				"read data pdu OK (offset %u, length %u)\n",
1772				data.offset, data.length);
1773
1774			/* Modify iov with offset and length. */
1775			iov = iov_ptr;
1776			(void) memcpy(iov, sg, sizeof(struct iovec) * sg_len);
1777			iov_len = sg_len;
1778			if (modify_iov(&iov, &iov_len, data.offset,
1779					data.length) != 0) {
1780				iscsi_err(__FILE__, __LINE__,
1781					"modify_iov() failed\n");
1782				TTD_CLEANUP;
1783				return -1;
1784			}
1785
1786			/* Scatter into destination buffers */
1787			if ((uint32_t)iscsi_sock_msg(sess->sock, 0,
1788				data.length, iov, iov_len) != data.length) {
1789				iscsi_err(__FILE__, __LINE__,
1790					"iscsi_sock_msg() failed\n");
1791				TTD_CLEANUP;
1792				return -1;
1793			}
1794			iscsi_trace(TRACE_ISCSI_DEBUG,
1795				"successfully scattered %u bytes\n",
1796				data.length);
1797			args->bytes_recv += data.length;
1798			desired_xfer_len -= data.length;
1799			if ((!r2t_flag) &&
1800			    (args->bytes_recv >
1801			    	sess->sess_params.first_burst_length)) {
1802				iscsi_err(__FILE__, __LINE__,
1803					"Received unsolicited data (%u) "
1804					"more than first_burst_length (%u)\n",
1805					args->bytes_recv,
1806					sess->sess_params.first_burst_length);
1807				args->status = 0x02;
1808				TTD_CLEANUP;
1809				return -1;
1810			}
1811			if ((desired_xfer_len != 0) && data.final) {
1812				iscsi_err(__FILE__, __LINE__,
1813					"Expecting more data (%d) "
1814					"from initiator for this sequence\n",
1815					desired_xfer_len);
1816				args->status = 0x02;
1817				TTD_CLEANUP;
1818				return -1;
1819			}
1820			if ((desired_xfer_len == 0) && !data.final) {
1821				iscsi_err(__FILE__, __LINE__,
1822					"Final bit not set on the last "
1823					"data PDU of this sequence\n");
1824				args->status = 0x02;
1825				TTD_CLEANUP;
1826				return -1;
1827			}
1828			if ((desired_xfer_len == 0) &&
1829			    (args->bytes_recv < args->trans_len)) {
1830				r2t_flag = 0;
1831			}
1832		} while (args->bytes_recv < args->trans_len);
1833#if 0
1834		RETURN_NOT_EQUAL("Final bit", data.final, 1, TTD_CLEANUP, -1);
1835#else
1836		if (data.final != 1) {
1837			iscsi_err(__FILE__, __LINE__, "Final bit\n");
1838			TTD_CLEANUP;
1839			return -1;
1840		}
1841#endif
1842	} else {
1843#if 0
1844		RETURN_NOT_EQUAL("Final bit", args->final, 1, TTD_CLEANUP, -1);
1845#else
1846		if (args->final != 1) {
1847			iscsi_err(__FILE__, __LINE__, "Final bit\n");
1848			TTD_CLEANUP;
1849			return -1;
1850		}
1851#endif
1852	}
1853	iscsi_trace(TRACE_ISCSI_DEBUG,
1854		"successfully transferred %u bytes write data\n",
1855		args->trans_len);
1856	TTD_CLEANUP;
1857	return 0;
1858}
1859
1860/* check there's enough space in the arrays */
1861static void
1862size_arrays(iscsi_target_t *tgt, unsigned needed)
1863{
1864	if (tgt->size == 0) {
1865		/* only get here first time around */
1866		tgt->size = needed;
1867		tgt->name = calloc(sizeof(char *), needed);
1868		tgt->value = calloc(sizeof(char *), needed);
1869	} else if (tgt->c == tgt->size) {
1870		/* only uses 'needed' when filled array */
1871		tgt->size += needed;
1872		tgt->name = realloc(tgt->name, sizeof(char *) * needed);
1873		tgt->value = realloc(tgt->value, sizeof(char *) * needed);
1874	}
1875}
1876
1877/* find the name in the array */
1878static int
1879findvar(iscsi_target_t *tgt, const char *name)
1880{
1881	unsigned	i;
1882
1883	for (i = 0 ; i < tgt->c && strcmp(tgt->name[i], name) != 0; i++) {
1884	}
1885	return (i == tgt->c) ? -1 : (int)i;
1886}
1887
1888/********************
1889 * Public Functions *
1890 ********************/
1891
1892int
1893iscsi_target_set_defaults(iscsi_target_t *tgt)
1894{
1895	char	buf[32];
1896
1897	/* set defaults */
1898	(void) memset(tgt, 0x0, sizeof(*tgt));
1899	iscsi_target_setvar(tgt, "iqn", DEFAULT_TARGET_NAME);
1900	(void) snprintf(buf, sizeof(buf), "%d", ISCSI_PORT);
1901	iscsi_target_setvar(tgt, "target port", buf);
1902	iscsi_target_setvar(tgt, "address family", "unspec");
1903	(void) snprintf(buf, sizeof(buf), "%d", DEFAULT_TARGET_MAX_SESSIONS);
1904	iscsi_target_setvar(tgt, "max sessions", buf);
1905	iscsi_target_setvar(tgt, "configfile", _PATH_ISCSI_TARGETS);
1906	iscsi_target_setvar(tgt, "blocklen", "512");
1907	return 1;
1908}
1909
1910/* re-read the configuration file */
1911int
1912iscsi_target_reconfigure(iscsi_target_t *tgt)
1913{
1914	targv_t	*oldluns;
1915	devv_t	*olddevices;
1916	extv_t	*oldextents;
1917	targv_t	*luns;
1918	devv_t	*devices;
1919	extv_t	*extents;
1920	char	*config;
1921
1922	NEW(targv_t, luns, "iscsi_target_reconf 1", return -1);
1923	NEW(devv_t, devices, "iscsi_target_reconf 2", return -1);
1924	NEW(extv_t, extents, "iscsi_target_reconf 3", return -1);
1925	config = iscsi_target_getvar(tgt, "configfile");
1926	if (!read_conf_file(config, tgt->lunv, tgt->devv, tgt->extentv)) {
1927		(void) fprintf(stderr, "Error: can't open `%s'\n", config);
1928		return 0;
1929	}
1930	/* it worked - let's reassign things */
1931	/* XXX - agc - lock */
1932	oldluns = tgt->lunv;
1933	olddevices = tgt->devv;
1934	oldextents = tgt->extentv;
1935	tgt->lunv = luns;
1936	tgt->devv = devices;
1937	tgt->extentv = extents;
1938	/* XXX - agc - unlock */
1939	/* free up storage */
1940	(void) free(oldluns);
1941	(void) free(olddevices);
1942	(void) free(oldextents);
1943	return 1;
1944}
1945
1946int
1947iscsi_target_start(iscsi_target_t *tgt)
1948{
1949	uint32_t	 j;
1950	targv_t		*lunv;
1951	char		*config;
1952	char		*dbg;
1953	int		 maxsessions;
1954	int              i;
1955
1956	if ((dbg = iscsi_target_getvar(tgt, "debug")) != NULL) {
1957		set_debug(dbg);
1958	}
1959	/* allocate space for disks, extents and targets */
1960	NEW(targv_t, tgt->lunv, "iscsi_target_start 1", return -1);
1961	NEW(devv_t, tgt->devv, "iscsi_target_start 2", return -1);
1962	NEW(extv_t, tgt->extentv, "iscsi_target_start 3", return -1);
1963	/* read the configuration file */
1964	config = iscsi_target_getvar(tgt, "configfile");
1965	if (!read_conf_file(config, tgt->lunv, tgt->devv, tgt->extentv)) {
1966		(void) fprintf(stderr, "Error: can't open `%s'\n", config);
1967		return 0;
1968	}
1969	lunv = tgt->lunv;
1970	if (lunv->c == 0) {
1971		(void) fprintf(stderr, "No targets to initialise\n");
1972		return -1;
1973	}
1974	maxsessions = atoi(iscsi_target_getvar(tgt, "max sessions"));
1975	NEWARRAY(target_session_t, g_session, maxsessions, "iscsi_target_start",
1976			return -1);
1977	device_set_var("blocklen", iscsi_target_getvar(tgt, "blocklen"));
1978	if (tgt->state == TARGET_INITIALIZING ||
1979	    tgt->state == TARGET_INITIALIZED) {
1980		iscsi_err(__FILE__, __LINE__,
1981			"duplicate target initialization attempted\n");
1982		return -1;
1983	}
1984	tgt->state = TARGET_INITIALIZING;
1985	if (iscsi_queue_init(&g_session_q, maxsessions) != 0) {
1986		iscsi_err(__FILE__, __LINE__,
1987			"iscsi_queue_init() failed\n");
1988		return -1;
1989	}
1990	tgt->main_pid = getpid();
1991	for (i = 0; i < maxsessions; i++) {
1992		g_session[i].id = i;
1993		g_session[i].d = -1;
1994		if (iscsi_queue_insert(&g_session_q, &g_session[i]) != 0) {
1995			iscsi_err(__FILE__, __LINE__,
1996				"iscsi_queue_insert() failed\n");
1997			return -1;
1998		}
1999	}
2000	for (j = 0 ; j < lunv->c ; j++) {
2001		int d = device_init(tgt, lunv, &lunv->v[j]);
2002
2003		if (d < 0) {
2004			iscsi_err(__FILE__, __LINE__,
2005				"device_init() failed\n");
2006			return -1;
2007		}
2008	}
2009	ISCSI_MUTEX_INIT(&g_session_q_mutex, return -1);
2010	tgt->listener_listening = 0;
2011	tgt->listener_pid = -1;
2012	tgt->state = TARGET_INITIALIZED;
2013	printf("TARGET: iSCSI Qualified Name (IQN) is %s\n",
2014			iscsi_target_getvar(tgt, "iqn"));
2015	for (i = 0 ; i < tgt->sockc ; i++) {
2016		printf("\tsocket %d listening on port %s\n", tgt->sockv[i],
2017			iscsi_target_getvar(tgt, "target port"));
2018	}
2019	return 0;
2020}
2021
2022int
2023iscsi_target_shutdown(iscsi_target_t *tgt)
2024{
2025	target_session_t	*sess;
2026	int			 maxsessions;
2027	int			 i;
2028
2029	if ((tgt->state == TARGET_SHUTTING_DOWN) ||
2030	    (tgt->state == TARGET_SHUT_DOWN)) {
2031		iscsi_err(__FILE__, __LINE__,
2032			"duplicate target shutdown attempted\n");
2033		return -1;
2034	}
2035	tgt->state = TARGET_SHUTTING_DOWN;
2036	iscsi_trace(TRACE_ISCSI_DEBUG, "shutting down target\n");
2037	maxsessions = atoi(iscsi_target_getvar(tgt, "max sessions"));
2038	for (i = 0; i < maxsessions; i++) {
2039		sess = &g_session[i];
2040
2041		/* Need to replace with a call to session_destroy() */
2042
2043		if (sess->IsLoggedIn) {
2044			printf("shutting down socket on sess %d\n", i);
2045			iscsi_trace(TRACE_ISCSI_DEBUG,
2046				"shutting down socket on sess %d\n", i);
2047			if (iscsi_sock_shutdown(sess->sock, 2) != 0) {
2048				iscsi_err(__FILE__, __LINE__,
2049					"iscsi_sock_shutdown() failed\n");
2050				return -1;
2051			}
2052			printf("waiting for worker %d (pid %d, state %d)\n",
2053				i, sess->worker.pid, sess->worker.state);
2054			iscsi_trace(TRACE_ISCSI_DEBUG,
2055				"waiting for worker %d (pid %d, state %d)\n",
2056				i, sess->worker.pid, sess->worker.state);
2057			while (sess->worker.state &
2058					ISCSI_WORKER_STATE_STARTED) {
2059				ISCSI_SPIN;
2060			}
2061			iscsi_trace(TRACE_ISCSI_DEBUG,
2062					"worker %d has exited\n", i);
2063		}
2064		if (device_shutdown(sess) != 0) {
2065			iscsi_err(__FILE__, __LINE__,
2066					"device_shutdown() failed\n");
2067			return -1;
2068		}
2069	}
2070	iscsi_trace(TRACE_ISCSI_DEBUG, "shutting down accept socket\n");
2071	if (iscsi_sock_shutdown(tgt->sockv[0], 2) != 0) {
2072		iscsi_err(__FILE__, __LINE__,
2073			"iscsi_sock_shutdown() failed\n");
2074		return -1;
2075	}
2076	if (tgt->listener_pid != getpid()) {
2077		iscsi_trace(TRACE_ISCSI_DEBUG, "waiting for listener thread\n");
2078		while (tgt->listener_listening) {
2079			ISCSI_SPIN;
2080		}
2081		iscsi_trace(TRACE_ISCSI_DEBUG, "listener thread has exited\n");
2082	}
2083	iscsi_trace(TRACE_ISCSI_DEBUG, "closing accept socket\n");
2084	if (iscsi_sock_close(tgt->sockv[0]) != 0) {
2085		iscsi_err(__FILE__, __LINE__,
2086			"iscsi_sock_close() failed\n");
2087		return -1;
2088	}
2089	ISCSI_MUTEX_DESTROY(&g_session_q_mutex, return -1);
2090	iscsi_trace(TRACE_ISCSI_DEBUG, "target shutdown complete\n");
2091	tgt->state = TARGET_SHUT_DOWN;
2092
2093	return 0;
2094}
2095
2096int
2097iscsi_target_listen(iscsi_target_t *tgt)
2098{
2099	struct sockaddr_in6	remoteAddrStorage6;
2100	struct sockaddr_in6	localAddrStorage6;
2101	struct sockaddr_in	remoteAddrStorage;
2102	struct sockaddr_in	localAddrStorage;
2103	target_session_t	*sess;
2104	socklen_t		remoteAddrLen;
2105	socklen_t		localAddrLen;
2106	char			targetaddress[1024];
2107	char			remote[1024];
2108	char			local[1024];
2109	char			*config;
2110	int			newconn;
2111	int			i;
2112
2113	ISCSI_THREAD_START("listen_thread");
2114	tgt->listener_pid = getpid();
2115	tgt->listener_listening++;
2116	iscsi_trace(TRACE_ISCSI_DEBUG, "listener thread started\n");
2117
2118	if (!iscsi_socks_establish(tgt->sockv, tgt->famv, &tgt->sockc,
2119			iscsi_target_getvar(tgt, "address family"),
2120			atoi(iscsi_target_getvar(tgt, "target port")))) {
2121		iscsi_err(__FILE__, __LINE__,
2122				"iscsi_sock_establish() failed\n");
2123		goto done;
2124	}
2125
2126	iscsi_trace(TRACE_NET_DEBUG, "create, bind, listen OK\n");
2127
2128	/* Loop for connections: FIX ME with queue */
2129
2130	while (tgt->state != TARGET_SHUT_DOWN) {
2131		ISCSI_LOCK(&g_session_q_mutex, return -1);
2132		if ((sess = iscsi_queue_remove(&g_session_q)) == NULL) {
2133			iscsi_err(__FILE__, __LINE__,
2134			"no free sessions: iscsi_queue_remove() failed\n");
2135			goto done;
2136		}
2137		ISCSI_UNLOCK(&g_session_q_mutex, return -1);
2138		assert(sess->d == -1);
2139#if 0
2140		(void) memset(sess, 0x0, sizeof(*sess));
2141#endif
2142
2143		sess->target = tgt;
2144
2145		/* Accept connection, spawn session thread, and */
2146		/* clean up old threads */
2147
2148		config = iscsi_target_getvar(tgt, "configfile");
2149		i = iscsi_waitfor_connection(tgt->sockv, tgt->sockc, config,
2150				&newconn);
2151
2152		iscsi_trace(TRACE_NET_DEBUG,
2153				"waiting for %s connection on port %s\n",
2154				iscsi_address_family(tgt->famv[i]),
2155				iscsi_target_getvar(tgt, "target port"));
2156
2157		if (!iscsi_sock_accept(newconn, &sess->sock)) {
2158			iscsi_trace(TRACE_ISCSI_DEBUG,
2159					"iscsi_sock_accept() failed\n");
2160			goto done;
2161		}
2162
2163		switch (tgt->famv[i]) {
2164		case AF_INET:
2165			sess->address_family = 4;
2166			(void) memset(&localAddrStorage, 0x0,
2167				localAddrLen = sizeof(localAddrStorage));
2168			if (getsockname(sess->sock,
2169				(struct sockaddr *)(void *)&localAddrStorage,
2170				&localAddrLen) < 0) {
2171				iscsi_err(__FILE__, __LINE__,
2172					"iscsi_sock_getsockname() failed\n");
2173				goto done;
2174			}
2175			(void) memset(&remoteAddrStorage, 0x0,
2176				remoteAddrLen = sizeof(remoteAddrStorage));
2177			if (getpeername(sess->sock,
2178				(struct sockaddr *)(void *) &remoteAddrStorage,
2179				&remoteAddrLen) < 0) {
2180				iscsi_err(__FILE__, __LINE__,
2181					"iscsi_sock_getpeername() failed\n");
2182				goto done;
2183			}
2184
2185#ifdef HAVE_GETNAMEINFO
2186			if (getnameinfo((struct sockaddr *)(void *)
2187				&localAddrStorage,
2188				sizeof(localAddrStorage), local,
2189				sizeof(local), NULL, 0, NI_NUMERICHOST) < 0) {
2190				iscsi_err(__FILE__, __LINE__,
2191					"getnameinfo local failed\n");
2192			}
2193			if (getnameinfo((struct sockaddr *)(void *)
2194				&remoteAddrStorage,
2195				sizeof(remoteAddrStorage), remote,
2196				sizeof(remote), NULL, 0, NI_NUMERICHOST) < 0) {
2197				iscsi_err(__FILE__, __LINE__,
2198					"getnameinfo remote failed\n");
2199			}
2200			(void) strlcpy(sess->initiator, remote,
2201					sizeof(sess->initiator));
2202#else
2203			(void) strlcpy(local,
2204				inet_ntoa(localAddrStorage.sin_addr),
2205				sizeof(local));
2206			(void) strlcpy(sess->initiator,
2207				inet_ntoa(remoteAddrStorage.sin_addr),
2208				sizeof(sess->initiator));
2209#endif
2210
2211			(void) snprintf(targetaddress, sizeof(targetaddress),
2212				"%s:%s,1", local,
2213				iscsi_target_getvar(tgt, "target port"));
2214			iscsi_target_setvar(tgt, "target address",
2215				targetaddress);
2216			iscsi_trace(TRACE_ISCSI_DEBUG,
2217				"IPv4 connection accepted on port %s "
2218				"(local IP %s, remote IP %s)\n",
2219				iscsi_target_getvar(tgt, "target port"),
2220				local, sess->initiator);
2221			iscsi_trace(TRACE_ISCSI_DEBUG,
2222				"TargetAddress = \"%s\"\n", targetaddress);
2223			break;
2224
2225		case AF_INET6:
2226			sess->address_family = 6;
2227			(void) memset(&localAddrStorage6, 0x0,
2228				localAddrLen = sizeof(localAddrStorage6));
2229			if (getsockname(sess->sock, (struct sockaddr *)(void *)
2230				&localAddrStorage6, &localAddrLen) < 0) {
2231				iscsi_err(__FILE__, __LINE__,
2232					"getsockname() failed\n");
2233				goto done;
2234			}
2235
2236			(void) memset(&remoteAddrStorage6, 0x0,
2237				remoteAddrLen = sizeof(remoteAddrStorage6));
2238			if (getpeername(sess->sock, (struct sockaddr *)(void *)
2239				&remoteAddrStorage6, &remoteAddrLen) < 0) {
2240				iscsi_err(__FILE__, __LINE__,
2241					"iscsi_sock_getpeername() failed\n");
2242				goto done;
2243			}
2244
2245			if (getnameinfo((struct sockaddr *)(void *)
2246				&localAddrStorage6, sizeof(localAddrStorage6),
2247				local, sizeof(local), NULL, 0,
2248				NI_NUMERICHOST) < 0) {
2249				iscsi_err(__FILE__, __LINE__,
2250					"getnameinfo local failed\n");
2251			}
2252			if (getnameinfo((struct sockaddr *)(void *)
2253				&remoteAddrStorage6,
2254				sizeof(remoteAddrStorage6), remote,
2255				sizeof(remote), NULL, 0, NI_NUMERICHOST) < 0) {
2256				iscsi_err(__FILE__, __LINE__,
2257					"getnameinfo remote failed\n");
2258			}
2259			(void) strlcpy(sess->initiator, remote,
2260				sizeof(sess->initiator));
2261			(void) snprintf(targetaddress, sizeof(targetaddress),
2262				"%s:%s,1", local,
2263				iscsi_target_getvar(tgt, "target port"));
2264			iscsi_target_setvar(tgt, "target address",
2265				targetaddress);
2266			iscsi_trace(TRACE_ISCSI_DEBUG,
2267				"IPv6 connection accepted on port %s "
2268				"(local IP %s, remote IP %s)\n",
2269				iscsi_target_getvar(tgt, "target port"),
2270				local, sess->initiator);
2271			iscsi_trace(TRACE_ISCSI_DEBUG,
2272				"TargetAddress = \"%s\"\n", targetaddress);
2273			break;
2274		}
2275		if (iscsi_thread_create(&sess->worker.thread,
2276			(void *) worker_proc_t, sess) != 0) {
2277			iscsi_err(__FILE__, __LINE__,
2278				"iscsi_thread_create() failed\n");
2279			goto done;
2280		}
2281	}
2282done:
2283	tgt->listener_listening--;
2284	return 0;
2285}
2286
2287/* write the pid to the pid file */
2288void
2289iscsi_target_write_pidfile(const char *f)
2290{
2291	FILE	*fp;
2292
2293	if (f == NULL) {
2294		f = _PATH_ISCSI_PID_FILE;
2295	}
2296	if ((fp = fopen(f, "w")) == NULL) {
2297		(void) fprintf(stderr, "Couldn't create pid file \"%s\": %s",
2298				f, strerror(errno));
2299	} else {
2300		(void) fprintf(fp, "%ld\n", (long) getpid());
2301		(void) fclose(fp);
2302	}
2303}
2304
2305/* set a variable */
2306int
2307iscsi_target_setvar(iscsi_target_t *tgt, const char *name, const char *value)
2308{
2309	int	i;
2310
2311	if ((i = findvar(tgt, name)) < 0) {
2312		/* add the element to the array */
2313		size_arrays(tgt, tgt->size + 15);
2314		tgt->name[i = tgt->c++] = strdup(name);
2315	} else {
2316		/* replace the element in the array */
2317		if (tgt->value[i]) {
2318			(void) free(tgt->value[i]);
2319			tgt->value[i] = NULL;
2320		}
2321	}
2322	/* sanity checks for range of values would go here */
2323	tgt->value[i] = strdup(value);
2324	return 1;
2325}
2326
2327/* get a variable's value (NULL if not set) */
2328char *
2329iscsi_target_getvar(iscsi_target_t *tgt, const char *name)
2330{
2331	int	i;
2332
2333	return ((i = findvar(tgt, name)) < 0) ? NULL : tgt->value[i];
2334}
2335