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