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