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