1/* $NetBSD: iscsic_main.c,v 1.4 2011/11/17 16:20:47 joerg Exp $ */ 2 3/*- 4 * Copyright (c) 2005,2006,2011 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Wasabi Systems, Inc. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31#include <sys/types.h> 32#include <sys/param.h> 33#include <sys/socket.h> 34#include <sys/un.h> 35 36#include "iscsic_globals.h" 37#include "iscsi_test.h" 38 39#include <err.h> 40#include <errno.h> 41#include <fcntl.h> 42#include <stdarg.h> 43 44#define DEVICE "/dev/iscsi0" 45 46#define ISCSICTL_VERSION "20110805" 47 48/* 49 *-------- Table of commands and the associated handler function ------------- 50*/ 51 52static command_t cmds[] = { 53 {"version", get_version}, 54 {"add_target", add_target}, 55 {"add_portal", add_portal}, 56 {"remove_target", remove_target}, 57 {"slp_find_targets", slp_find_targets}, 58 {"refresh_targets", refresh_targets}, 59 {"list_targets", list_targets}, 60 {"add_send_target", add_send_target}, 61 {"remove_send_target", remove_send_target}, 62 {"list_send_targets", list_send_targets}, 63 {"add_isns_server", add_isns_server}, 64 {"remove_isns_server", remove_isns_server}, 65 {"find_isns_servers", find_isns_servers}, 66 {"list_isns_servers", list_isns_servers}, 67 {"refresh_isns", refresh_isns}, 68 {"login", login}, 69 {"logout", logout}, 70 {"add_connection", add_connection}, 71 {"remove_connection", remove_connection}, 72 {"inquiry", inquiry}, 73 {"read_capacity", read_capacity}, 74 {"report_luns", report_luns}, 75 {"test_unit_ready", test_unit_ready}, 76 {"add_initiator", add_initiator}, 77 {"remove_initiator", remove_initiator}, 78 {"list_initiators", list_initiators}, 79 {"list_sessions", list_sessions}, 80 {"set_node_name", set_node_name}, 81#ifdef ISCSI_DEBUG 82 {"kill_daemon", kill_daemon}, 83#endif 84#ifdef ISCSI_TEST_MODE 85 {"test", test}, 86#endif 87 {NULL, NULL} 88}; 89 90 91/* 92 *-------- Table of error codes and the associated message text ------------- 93*/ 94 95typedef struct { 96 unsigned code; 97 const char *str; 98} status_msg_t; 99 100static status_msg_t status_msg[] = { 101 {ISCSI_STATUS_LIST_EMPTY, "The list is empty"}, 102 {ISCSI_STATUS_DUPLICATE_NAME, "The specified name is not unique"}, 103 {ISCSI_STATUS_GENERAL_ERROR, "A non-specific error occurred"}, 104 {ISCSI_STATUS_LOGIN_FAILED, "The login failed"}, 105 {ISCSI_STATUS_CONNECTION_FAILED, "The attempt to establish a connection failed"}, 106 {ISCSI_STATUS_AUTHENTICATION_FAILED, "Authentication negotiation failed"}, 107 {ISCSI_STATUS_NO_RESOURCES, "Could not allocate resources (e.g. memory)"}, 108 {ISCSI_STATUS_MAXED_CONNECTIONS, "Maximum number of connections exceeded"}, 109 {ISCSI_STATUS_INVALID_SESSION_ID, "Session ID not found"}, 110 {ISCSI_STATUS_INVALID_CONNECTION_ID, "Connection ID not found"}, 111 {ISCSI_STATUS_INVALID_SOCKET, "Specified socket is invalid"}, 112 {ISCSI_STATUS_NOTIMPL, "Feature not implemented"}, 113 {ISCSI_STATUS_CHECK_CONDITION, "Target reported CHECK CONDITION"}, 114 {ISCSI_STATUS_TARGET_BUSY, "Target reported BUSY"}, 115 {ISCSI_STATUS_TARGET_ERROR, "Target reported other error"}, 116 {ISCSI_STATUS_TARGET_FAILURE, "Command Response was Target Failure"}, 117 {ISCSI_STATUS_TARGET_DROP, "Target dropped connection"}, 118 {ISCSI_STATUS_SOCKET_ERROR, "Communication failure"}, 119 {ISCSI_STATUS_PARAMETER_MISSING, "A required ioctl parameter is missing"}, 120 {ISCSI_STATUS_PARAMETER_INVALID, "A parameter is malformed (string too long etc.)"}, 121 {ISCSI_STATUS_MAP_FAILED, "Mapping the LUNs failed"}, 122 {ISCSI_STATUS_NO_INITIATOR_NAME, "Initiator name not set"}, 123 {ISCSI_STATUS_NEGOTIATION_ERROR, "Negotiation failure (invalid key or value)"}, 124 {ISCSI_STATUS_TIMEOUT, "Command timed out (at iSCSI level)"}, 125 {ISCSI_STATUS_PROTOCOL_ERROR, "Internal Error (Protocol error reject)"}, 126 {ISCSI_STATUS_PDU_ERROR, "Internal Error (Invalid PDU field reject)"}, 127 {ISCSI_STATUS_CMD_NOT_SUPPORTED, "Target does not support iSCSI command"}, 128 {ISCSI_STATUS_DRIVER_UNLOAD, "Driver is unloading"}, 129 {ISCSI_STATUS_LOGOUT, "Session was logged out"}, 130 {ISCSI_STATUS_PDUS_LOST, "Excessive PDU loss"}, 131 {ISCSI_STATUS_INVALID_EVENT_ID, "Invalid Event ID"}, 132 {ISCSI_STATUS_EVENT_DEREGISTERED, "Wait for event cancelled by deregistration"}, 133 {ISCSI_STATUS_EVENT_WAITING, "Already waiting for event"}, 134 {ISCSI_STATUS_TASK_NOT_FOUND, "Task Management: task not found"}, 135 {ISCSI_STATUS_LUN_NOT_FOUND, "Task Management: LUN not found"}, 136 {ISCSI_STATUS_TASK_ALLEGIANT, "Task Management: Task still allegiant"}, 137 {ISCSI_STATUS_CANT_REASSIGN, "Task Management: Task reassignment not supported"}, 138 {ISCSI_STATUS_FUNCTION_UNSUPPORTED, "Task Management: Function unsupported"}, 139 {ISCSI_STATUS_FUNCTION_NOT_AUTHORIZED, "Task Management: Function not authorized"}, 140 {ISCSI_STATUS_FUNCTION_REJECTED, "Task Management: Function rejected"}, 141 {ISCSI_STATUS_UNKNOWN_REASON, "Task Management: Unknown reason code"}, 142 {ISCSI_STATUS_DUPLICATE_ID, "Duplicate ID"}, 143 {ISCSI_STATUS_INVALID_ID, "ID not found"}, 144 {ISCSI_STATUS_TARGET_LOGOUT, "Target requested logout"}, 145 {ISCSI_STATUS_LOGOUT_CID_NOT_FOUND, "Logout error: Connection ID not found"}, 146 {ISCSI_STATUS_LOGOUT_RECOVERY_NS, "Logout error: Recovery not supported"}, 147 {ISCSI_STATUS_LOGOUT_ERROR, "Logout error: Unknown reason"}, 148 149 {ISCSID_STATUS_LIST_EMPTY, "The list is empty"}, 150 {ISCSID_STATUS_DUPLICATE_NAME, "The specified name is not unique"}, 151 {ISCSID_STATUS_GENERAL_ERROR, "A non-specific error occurred"}, 152 {ISCSID_STATUS_CONNECT_ERROR, "Failed to connect to target"}, 153 {ISCSID_STATUS_NO_RESOURCES, "Could not allocate resources (e.g. memory)"}, 154 {ISCSID_STATUS_INVALID_SESSION_ID, "Session ID not found"}, 155 {ISCSID_STATUS_INVALID_CONNECTION_ID, "Connection ID not found"}, 156 {ISCSID_STATUS_NOTIMPL, "Feature not implemented"}, 157 {ISCSID_STATUS_SOCKET_ERROR, "Failed to create socket"}, 158 {ISCSID_STATUS_PARAMETER_MISSING, "A required parameter is missing"}, 159 {ISCSID_STATUS_PARAMETER_INVALID, "Request parameter is invalid"}, 160 {ISCSID_STATUS_NO_INITIATOR_NAME, "Initiator name was not set"}, 161 {ISCSID_STATUS_TIMEOUT, "Request timed out"}, 162 {ISCSID_STATUS_DRIVER_NOT_LOADED, "iSCSI Driver not loaded"}, 163 {ISCSID_STATUS_INVALID_REQUEST, "Unknown request code"}, 164 {ISCSID_STATUS_INVALID_PORTAL_ID, "Portal ID not found"}, 165 {ISCSID_STATUS_INVALID_TARGET_ID, "Target ID not found"}, 166 {ISCSID_STATUS_NOT_FOUND, "Requested item not found"}, 167 {ISCSID_STATUS_HOST_NOT_FOUND, "Target address not found"}, 168 {ISCSID_STATUS_HOST_TRY_AGAIN, "Target address retrieval failed, try again later"}, 169 {ISCSID_STATUS_HOST_ERROR, "Target address invalid"}, 170 {ISCSID_STATUS_NO_TARGETS_FOUND, "No targets found"}, 171 {ISCSID_STATUS_INVALID_ISNS_ID, "iSNS ID not found"}, 172 {ISCSID_STATUS_ISNS_ERROR, "Problem connecting to iSNS"}, 173 {ISCSID_STATUS_ISNS_SERVER_ERROR, "iSNS server returned garbage"}, 174 {ISCSID_STATUS_DUPLICATE_ENTRY, "The entry already exists"}, 175 {ISCSID_STATUS_INVALID_INITIATOR_ID, "Initiator ID not found"}, 176 {ISCSID_STATUS_INITIATOR_BIND_ERROR, "Bind to initiator portal failed"}, 177 178#ifdef ISCSI_TEST_MODE 179 {ISCSI_STATUS_TEST_INACTIVE, "Test not assigned to connection"}, 180 {ISCSI_STATUS_TEST_CANCELED, "Test was cancelled"}, 181 {ISCSI_STATUS_TEST_CONNECTION_CLOSED, "Connection closed"}, 182 {ISCSI_STATUS_TEST_MODIFICATION_SKIPPED, "Modification skipped due to bad offset"}, 183 {ISCSI_STATUS_TEST_HEADER_CRC_ERROR, "Received PDU had bad header CRC"}, 184 {ISCSI_STATUS_TEST_DATA_CRC_ERROR, "Received PDU had bad data CRC"}, 185 {ISCSI_STATUS_TEST_DATA_READ_ERROR, "Error while receiving PDU"}, 186#endif 187 188 {0, NULL} 189}; 190 191/* -------------------------------------------------------------------------- */ 192/* local variables */ 193 194static struct sockaddr_un daemon_name; /* daemon socket name */ 195 196static char sockdir[MAXPATHLEN]; /* where myname lives */ 197static struct sockaddr_un myname; /* my socket name */ 198static int sock; /* the socket */ 199 200static char *cmdname; /* pointer to command name for error msgs */ 201 202/* global variables */ 203 204uint8_t buf[BUF_SIZE]; /* buffer for daemon comm and driver I/O */ 205 206int driver; /* driver handle */ 207 208#ifdef ISCSI_DEBUG 209int debug_level = ISCSI_DEBUG; /* How much info to display */ 210#endif 211 212/* -------------------------------------------------------------------------- */ 213 214#define progname getprogname() 215 216 217/* 218 * bye: 219 * Cleanup and exit. Does not return. 220*/ 221 222__dead static void 223bye(void) 224{ 225 close(sock); 226 (void) unlink(myname.sun_path); 227 (void) rmdir(sockdir); 228 exit(EXIT_FAILURE); 229} 230 231 232/* 233 * arg_error: 234 * Display error message about an invalid argument, exit. 235 * 236 * Parameters: 237 * argp Argument value 238 * fmt Error message 239 * ... additional output arguments 240 * 241 * Does not return. 242*/ 243 244void 245arg_error(char *argp, const char *fmt, ...) 246{ 247 va_list args; 248 char lbuf[BUF_SIZE]; 249 250 va_start(args, fmt); 251 vsnprintf(lbuf, sizeof(lbuf), fmt, args); 252 fprintf(stderr, "%s: %s: Invalid option at or near '%s': %s\n", 253 progname, cmdname, argp, lbuf); 254 bye(); 255} 256 257 258/* 259 * arg_missing: 260 * Display error message about a missing argument, exit. 261 * 262 * Parameters: 263 * arg Argument name 264 * 265 * Does not return. 266 */ 267 268void 269arg_missing(const char *arg) 270{ 271 warnx("%s: Missing argument: %s", cmdname, arg); 272 bye(); 273} 274 275 276/* 277 * io_error: 278 * Display error message about an I/O error (includes system error code) 279 * 280 * Parameters: 281 * fmt format string 282 * ... additional output arguments 283 * 284 * Does not return. 285 */ 286 287void 288io_error(const char *fmt, ...) 289{ 290 va_list args; 291 char lbuf[BUF_SIZE]; 292 293 va_start(args, fmt); 294 vsnprintf(lbuf, sizeof(lbuf), fmt, args); 295 fprintf(stderr, "%s: %s: %s: %s\n", 296 progname, cmdname, lbuf, strerror(errno)); 297 bye(); 298} 299 300 301/* 302 * gen_error: 303 * Display general error message. 304 * 305 * Parameters: 306 * fmt format string 307 * ... additional output arguments 308 * 309 * Does not return. 310 */ 311 312void 313gen_error(const char *fmt, ...) 314{ 315 va_list args; 316 char lbuf[BUF_SIZE]; 317 318 va_start(args, fmt); 319 vsnprintf(lbuf, sizeof(lbuf), fmt, args); 320 fprintf(stderr, "%s: %s: %s\n", progname, cmdname, lbuf); 321 bye(); 322} 323 324 325/* 326 * check_extra_args: 327 * Display error message & exit if there is an unrecognized argument. 328 * 329 * Parameters: 330 * argc Argument count 331 * argv Argument value array. 332 * 333 * Does not return if an extra arg is found. 334 */ 335 336void 337check_extra_args(int argc, char **argv) 338{ 339 int i; 340 341 for (i = 0; i < argc; i++) 342 if (argv[i] != NULL) { 343 warnx("%s: Unrecognized argument '%s'", cmdname, argv[i]); 344 bye(); 345 } 346} 347 348 349/* 350 * status_error: 351 * Display error message for status returned by daemon or driver, exit. 352 * 353 * Parameters: 354 * n Status code. 355 * 356 * Does not return. 357 */ 358 359void 360status_error(unsigned n) 361{ 362 int i; 363 364 for (i = 0; status_msg[i].code; i++) 365 if (status_msg[i].code == n) 366 break; 367 368 if (status_msg[i].code) 369 warnx("%s: %s", cmdname, status_msg[i].str); 370 else 371 warnx("%s: Undefined error code %d", cmdname, n); 372 373 bye(); 374} 375 376 377/* 378 * status_error_slist: 379 * Display error message for status returned by daemon or driver, but 380 * replace a "list is empty" code by an "ID not found" code, exit. 381 * 382 * Parameters: 383 * n Status code. 384 * 385 * Does not return. 386 */ 387 388void 389status_error_slist(unsigned n) 390{ 391 if (n == ISCSI_STATUS_LIST_EMPTY || n == ISCSID_STATUS_LIST_EMPTY) 392 n = ISCSI_STATUS_INVALID_ID; 393 status_error (n); 394} 395 396 397/* 398 * get_response: 399 * Read the response from the daemon. 400 * 401 * Parameters: 402 * temp If TRUE, the response is dynamically allocated (so it is not 403 * overwritten by further requests or responses). 404 * 405 * Returns: 406 * Pointer to the response. 407 * 408 * Notes: 409 * This routine allocates an extra integer to mark whether the returned 410 * buffer is dynamic or static. This marker is one int before the 411 * returned pointer. 412 */ 413 414iscsid_response_t * 415get_response(int temp) 416{ 417 ssize_t ret; 418 size_t len; 419 iscsid_response_t *rsp; 420 int *pbuf; 421 422 pbuf = (int *)(void *)buf; 423 rsp = (iscsid_response_t *)(void *)&pbuf[1]; 424 *pbuf = 0; 425 426 /* get size of response */ 427 len = sizeof(iscsid_response_t); 428 ret = recv(sock, rsp, len, MSG_PEEK | MSG_WAITALL); 429 if ((size_t)ret != len) 430 io_error("Receiving daemon data"); 431 432 DEB(9, ("Status %d, parlen %d\n", rsp->status, rsp->parameter_length)); 433 434 len += rsp->parameter_length; 435 436 /* 437 if a temp buffer has been requested, or if the response is too large 438 to fit into the static buffer, alloc a temp buffer. 439 */ 440 441 temp = temp || (len > (int)(sizeof(buf) - sizeof(int))); 442 443 if (temp) { 444 if (NULL == (pbuf = (int *) malloc(len + sizeof(int)))) 445 gen_error("Can't allocate response buffer (%zu bytes)", 446 len + sizeof(int)); 447 448 rsp = (iscsid_response_t *)(void *)&pbuf[1]; 449 *pbuf = 1; 450 } 451 /* get the complete response */ 452 453 ret = recv(sock, rsp, len, MSG_WAITALL); 454 if ((size_t)ret != len) 455 io_error("Receiving daemon data"); 456 457 return rsp; 458} 459 460 461/* 462 * free_response: 463 * If the response buffer was dynamically allocated, free it. 464 * 465 * Parameters: 466 * rsp The response buffer. 467 * The dynamic allocation marker is the int preceding 468 * this address. 469 */ 470 471void 472free_response(iscsid_response_t * rsp) 473{ 474 int *pbuf; 475 476 pbuf = ((int *)(void *)rsp) - 1; 477 if (*pbuf) 478 free(pbuf); 479} 480 481 482/* 483 * send_request: 484 * Send a request to the daemon. 485 * 486 * Parameters: 487 * request The request code. 488 * par_len The parameter length. 489 * par The parameter. 490 */ 491 492void 493send_request(unsigned request, size_t par_len, void *par) 494{ 495 iscsid_request_t *req; 496 size_t len; 497 ssize_t ret; 498 int req_temp; 499 500 len = sizeof(iscsid_request_t) + par_len; 501 502 /* alloc buffer if static one is too small to hold request */ 503 req_temp = len > sizeof(buf); 504 505 if (req_temp) { 506 req = malloc(len); 507 if (req == NULL) 508 gen_error("Out of memory allocating %zu bytes\n", len); 509 } else 510 req = (iscsid_request_t *)(void *)buf; 511 512 /* setup request */ 513 req->request = request; 514 req->parameter_length = (uint32_t)par_len; 515 if (par_len) 516 memcpy(req->parameter, par, par_len); 517 518 /* and send it out */ 519 ret = sendto(sock, req, len, 0, (struct sockaddr *)(void *)&daemon_name, 520 (socklen_t)sizeof(struct sockaddr_un)); 521 if ((size_t)ret != len) { 522 io_error("Sending daemon message"); 523 } 524 if (req_temp) 525 free(req); 526} 527 528 529/* 530 * main: 531 * check command, init driver handle and socket, dispatch command. 532 * 533 * Parameter: argc, argv - passed on to commands with offset of 2, so 534 * argv [0] is first argument after command verb. 535 * 536 * Returns: 537 * Whatever the command handler returns, which is currently always 0. 538 */ 539 540int 541main(int argc, char **argv) 542{ 543 command_t *c; 544 int res; 545 int i; 546 547 (void) snprintf(sockdir, sizeof(sockdir), "/tmp/iscsictl.XXXXXX"); 548 while ((i = getopt(argc, argv, "d:")) != -1) { 549 switch(i) { 550 case 'd': 551 (void) snprintf(sockdir, sizeof(sockdir), "%s", optarg); 552 break; 553 default: 554 break; 555 } 556 } 557 if (argc - optind < 1) { 558 errx(EXIT_FAILURE, "Usage: %s <command> <options>, see manual for details.", 559 progname); 560 } 561 562 cmdname = argv[optind]; 563 564 for (c = cmds; c->cmd != NULL; c++) { 565 if (strcmp(c->cmd, cmdname) == 0) { 566 break; 567 } 568 } 569 if (c->cmd == NULL) { 570 errx(EXIT_FAILURE, "Unknown command: '%s'", cmdname); 571 } 572 if ((driver = open(DEVICE, O_RDONLY)) < 0) { 573 warn("Opening " DEVICE); 574#ifndef ISCSI_DEBUG 575 /* DEBUG ONLY: Allow CLI to operate w/o driver */ 576 return EXIT_FAILURE; 577#endif 578 } 579 580 sock = socket(AF_UNIX, SOCK_DGRAM, 0); 581 if (sock < 0) 582 err(EXIT_FAILURE, "opening datagram socket"); 583 584 /* bind socket to unique name */ 585 if (mkdtemp(sockdir) == NULL) { 586 errx(EXIT_FAILURE, "can't create iscsictl dir '%s'", sockdir); 587 } 588 myname.sun_family = AF_UNIX; 589 (void) snprintf(myname.sun_path, sizeof(myname.sun_path), "%s/socket", sockdir); 590 if (bind(sock, (struct sockaddr *)(void *)&myname, 591 (socklen_t)sizeof(struct sockaddr_un)) < 0) { 592 io_error("Binding name to datagram socket"); 593 } 594 daemon_name.sun_family = AF_UNIX; 595 strlcpy(daemon_name.sun_path, ISCSID_SOCK_NAME, 596 sizeof(daemon_name.sun_path)); 597 598 /* dispatch command */ 599 res = (*c->proc)(argc - optind - 1, &argv[optind + 1]); 600 601 /* cleanup */ 602 close(sock); 603 unlink(myname.sun_path); 604 rmdir(sockdir); 605 606 return res; 607} 608