1 /* 2 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 /* 7 * BSD 3 Clause License 8 * 9 * Copyright (c) 2007, The Storage Networking Industry Association. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * - Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 17 * - Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in 19 * the documentation and/or other materials provided with the 20 * distribution. 21 * 22 * - Neither the name of The Storage Networking Industry Association (SNIA) 23 * nor the names of its contributors may be used to endorse or promote 24 * products derived from this software without specific prior written 25 * permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 28 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 31 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 * POSSIBILITY OF SUCH DAMAGE. 38 */ 39 /* Copyright (c) 2007, The Storage Networking Industry Association. */ 40 /* Copyright (c) 1996, 1997 PDC, Network Appliance. All Rights Reserved */ 41 42 #include <sys/types.h> 43 #include <ctype.h> 44 #include <errno.h> 45 #include <fcntl.h> 46 #include <stdlib.h> 47 #include "ndmpd_common.h" 48 #include "ndmpd.h" 49 #include <string.h> 50 #include <sys/scsi/impl/uscsi.h> 51 #include <sys/scsi/scsi.h> 52 53 static void scsi_open_send_reply(ndmp_connection_t *connection, int err); 54 static void common_open(ndmp_connection_t *connection, char *devname); 55 static void common_set_target(ndmp_connection_t *connection, char *device, 56 ushort_t controller, ushort_t sid, ushort_t lun); 57 58 59 /* 60 * ************************************************************************ 61 * NDMP V2 HANDLERS 62 * ************************************************************************ 63 */ 64 65 /* 66 * ndmpd_scsi_open_v2 67 * 68 * This handler opens the specified SCSI device. 69 * 70 * Parameters: 71 * connection (input) - connection handle. 72 * body (input) - request message body. 73 * 74 * Returns: 75 * void 76 */ 77 void 78 ndmpd_scsi_open_v2(ndmp_connection_t *connection, void *body) 79 { 80 ndmp_scsi_open_request_v2 *request = (ndmp_scsi_open_request_v2 *)body; 81 82 common_open(connection, request->device.name); 83 } 84 85 86 /* 87 * ndmpd_scsi_close_v2 88 * 89 * This handler closes the currently open SCSI device. 90 * 91 * Parameters: 92 * connection (input) - connection handle. 93 * body (input) - request message body. 94 * 95 * Returns: 96 * void 97 */ 98 /*ARGSUSED*/ 99 void 100 ndmpd_scsi_close_v2(ndmp_connection_t *connection, void *body) 101 { 102 ndmp_scsi_close_reply reply; 103 ndmpd_session_t *session = ndmp_get_client_data(connection); 104 105 if (session->ns_scsi.sd_is_open == -1) { 106 NDMP_LOG(LOG_ERR, "SCSI device is not open."); 107 reply.error = NDMP_DEV_NOT_OPEN_ERR; 108 ndmp_send_reply(connection, (void *) &reply, 109 "sending scsi_close reply"); 110 return; 111 } 112 (void) ndmp_open_list_del(session->ns_scsi.sd_adapter_name, 113 session->ns_scsi.sd_sid, 114 session->ns_scsi.sd_lun); 115 (void) close(session->ns_scsi.sd_devid); 116 117 session->ns_scsi.sd_is_open = -1; 118 session->ns_scsi.sd_devid = -1; 119 session->ns_scsi.sd_sid = 0; 120 session->ns_scsi.sd_lun = 0; 121 session->ns_scsi.sd_valid_target_set = FALSE; 122 (void) memset(session->ns_scsi.sd_adapter_name, 0, 123 sizeof (session->ns_scsi.sd_adapter_name)); 124 125 reply.error = NDMP_NO_ERR; 126 ndmp_send_reply(connection, (void *) &reply, 127 "sending scsi_close reply"); 128 } 129 130 131 /* 132 * ndmpd_scsi_get_state_v2 133 * 134 * This handler returns state information for the currently open SCSI device. 135 * Since the implementation only supports the opening of a specific SCSI 136 * device, as opposed to a device that can talk to multiple SCSI targets, 137 * this request is not supported. This request is only appropriate for 138 * implementations that support device files that can target multiple 139 * SCSI devices. 140 * 141 * Parameters: 142 * connection (input) - connection handle. 143 * body (input) - request message body. 144 * 145 * Returns: 146 * void 147 */ 148 /*ARGSUSED*/ 149 void 150 ndmpd_scsi_get_state_v2(ndmp_connection_t *connection, void *body) 151 { 152 ndmp_scsi_get_state_reply reply; 153 ndmpd_session_t *session = ndmp_get_client_data(connection); 154 155 if (session->ns_scsi.sd_is_open == -1) 156 reply.error = NDMP_DEV_NOT_OPEN_ERR; 157 else if (!session->ns_scsi.sd_valid_target_set) { 158 reply.error = NDMP_NO_ERR; 159 reply.target_controller = -1; 160 reply.target_id = -1; 161 reply.target_lun = -1; 162 } else { 163 reply.error = NDMP_NO_ERR; 164 reply.target_controller = 0; 165 reply.target_id = session->ns_scsi.sd_sid; 166 reply.target_lun = session->ns_scsi.sd_lun; 167 } 168 169 ndmp_send_reply(connection, (void *) &reply, 170 "sending scsi_get_state reply"); 171 } 172 173 174 /* 175 * ndmpd_scsi_set_target_v2 176 * 177 * This handler sets the SCSI target of the SCSI device. 178 * It is only valid to use this request if the opened SCSI device 179 * is capable of talking to multiple SCSI targets. 180 * Since the implementation only supports the opening of a specific SCSI 181 * device, as opposed to a device that can talk to multiple SCSI targets, 182 * this request is not supported. This request is only appropriate for 183 * implementations that support device files that can target multiple 184 * SCSI devices. 185 * 186 * Parameters: 187 * connection (input) - connection handle. 188 * body (input) - request message body. 189 * 190 * Returns: 191 * void 192 */ 193 void 194 ndmpd_scsi_set_target_v2(ndmp_connection_t *connection, void *body) 195 { 196 ndmp_scsi_set_target_request_v2 *request; 197 198 request = (ndmp_scsi_set_target_request_v2 *) body; 199 200 common_set_target(connection, request->device.name, 201 request->target_controller, request->target_id, 202 request->target_lun); 203 } 204 205 206 /* 207 * ndmpd_scsi_reset_device_v2 208 * 209 * This handler resets the currently targeted SCSI device. 210 * 211 * Parameters: 212 * connection (input) - connection handle. 213 * body (input) - request message body. 214 * 215 * Returns: 216 * void 217 */ 218 /*ARGSUSED*/ 219 void 220 ndmpd_scsi_reset_device_v2(ndmp_connection_t *connection, void *body) 221 { 222 ndmp_scsi_reset_device_reply reply; 223 224 225 ndmpd_session_t *session = ndmp_get_client_data(connection); 226 struct uscsi_cmd cmd; 227 228 if (session->ns_scsi.sd_devid == -1) { 229 NDMP_LOG(LOG_ERR, "SCSI device is not open."); 230 reply.error = NDMP_DEV_NOT_OPEN_ERR; 231 } else { 232 reply.error = NDMP_NO_ERR; 233 (void) memset((void*)&cmd, 0, sizeof (cmd)); 234 cmd.uscsi_flags |= USCSI_RESET; 235 if (ioctl(session->ns_scsi.sd_devid, USCSICMD, &cmd) < 0) { 236 NDMP_LOG(LOG_ERR, "USCSI reset failed: %m."); 237 NDMP_LOG(LOG_DEBUG, 238 "ioctl(USCSICMD) USCSI_RESET failed: %m."); 239 reply.error = NDMP_IO_ERR; 240 } 241 } 242 243 ndmp_send_reply(connection, (void *) &reply, 244 "sending scsi_reset_device reply"); 245 } 246 247 248 /* 249 * ndmpd_scsi_reset_bus_v2 250 * 251 * This handler resets the currently targeted SCSI bus. 252 * 253 * Request not yet supported. 254 * 255 * Parameters: 256 * connection (input) - connection handle. 257 * body (input) - request message body. 258 * 259 * Returns: 260 * void 261 */ 262 /*ARGSUSED*/ 263 void 264 ndmpd_scsi_reset_bus_v2(ndmp_connection_t *connection, void *body) 265 { 266 ndmp_scsi_reset_bus_reply reply; 267 268 NDMP_LOG(LOG_DEBUG, "request not supported"); 269 reply.error = NDMP_NOT_SUPPORTED_ERR; 270 271 ndmp_send_reply(connection, (void *) &reply, 272 "sending scsi_reset_bus reply"); 273 } 274 275 276 /* 277 * ndmpd_scsi_execute_cdb_v2 278 * 279 * This handler sends the CDB to the currently targeted SCSI device. 280 * 281 * Parameters: 282 * connection (input) - connection handle. 283 * body (input) - request message body. 284 * 285 * Returns: 286 * void 287 */ 288 void 289 ndmpd_scsi_execute_cdb_v2(ndmp_connection_t *connection, void *body) 290 { 291 ndmp_execute_cdb_request *request = (ndmp_execute_cdb_request *) body; 292 ndmp_execute_cdb_reply reply; 293 ndmpd_session_t *session = ndmp_get_client_data(connection); 294 295 if (session->ns_scsi.sd_is_open == -1 || 296 !session->ns_scsi.sd_valid_target_set) { 297 (void) memset((void *) &reply, 0, sizeof (reply)); 298 299 NDMP_LOG(LOG_ERR, "SCSI device is not open."); 300 reply.error = NDMP_DEV_NOT_OPEN_ERR; 301 ndmp_send_reply(connection, (void *) &reply, 302 "sending scsi_execute_cdb reply"); 303 } else { 304 ndmp_execute_cdb(session, session->ns_scsi.sd_adapter_name, 305 session->ns_scsi.sd_sid, session->ns_scsi.sd_lun, request); 306 } 307 } 308 309 310 /* 311 * ************************************************************************ 312 * NDMP V3 HANDLERS 313 * ************************************************************************ 314 */ 315 316 /* 317 * ndmpd_scsi_open_v3 318 * 319 * This handler opens the specified SCSI device. 320 * 321 * Parameters: 322 * connection (input) - connection handle. 323 * body (input) - request message body. 324 * 325 * Returns: 326 * void 327 */ 328 void 329 ndmpd_scsi_open_v3(ndmp_connection_t *connection, void *body) 330 { 331 ndmp_scsi_open_request_v3 *request = (ndmp_scsi_open_request_v3 *)body; 332 333 common_open(connection, request->device); 334 } 335 336 337 /* 338 * ndmpd_scsi_set_target_v3 339 * 340 * This handler sets the SCSI target of the SCSI device. 341 * It is only valid to use this request if the opened SCSI device 342 * is capable of talking to multiple SCSI targets. 343 * 344 * Parameters: 345 * connection (input) - connection handle. 346 * body (input) - request message body. 347 * 348 * Returns: 349 * void 350 */ 351 void 352 ndmpd_scsi_set_target_v3(ndmp_connection_t *connection, void *body) 353 { 354 ndmp_scsi_set_target_request_v3 *request; 355 356 request = (ndmp_scsi_set_target_request_v3 *) body; 357 358 common_set_target(connection, request->device, 359 request->target_controller, request->target_id, 360 request->target_lun); 361 } 362 363 364 /* 365 * ************************************************************************ 366 * NDMP V4 HANDLERS 367 * ************************************************************************ 368 */ 369 370 /* 371 * ************************************************************************ 372 * LOCALS 373 * ************************************************************************ 374 */ 375 376 377 /* 378 * scsi_open_send_reply 379 * 380 * Send a reply for SCSI open command 381 * 382 * Parameters: 383 * connection (input) - connection handle. 384 * err (input) - ndmp error code 385 * 386 * Returns: 387 * void 388 */ 389 static void 390 scsi_open_send_reply(ndmp_connection_t *connection, int err) 391 { 392 ndmp_scsi_open_reply reply; 393 394 reply.error = err; 395 ndmp_send_reply(connection, (void *) &reply, "sending scsi_open reply"); 396 } 397 398 399 /* 400 * common_open 401 * 402 * Common SCSI open function for all NDMP versions 403 * 404 * Parameters: 405 * connection (input) - connection handle. 406 * devname (input) - device name to open. 407 * 408 * Returns: 409 * void 410 */ 411 static void 412 common_open(ndmp_connection_t *connection, char *devname) 413 { 414 ndmpd_session_t *session = ndmp_get_client_data(connection); 415 char adptnm[SCSI_MAX_NAME]; 416 int sid, lun; 417 int err; 418 scsi_adapter_t *sa; 419 int devid; 420 421 err = NDMP_NO_ERR; 422 423 if (session->ns_tape.td_fd != -1 || session->ns_scsi.sd_is_open != -1) { 424 NDMP_LOG(LOG_ERR, 425 "Session already has a tape or scsi device open."); 426 err = NDMP_DEVICE_OPENED_ERR; 427 } else if ((sa = scsi_get_adapter(0)) != NULL) { 428 NDMP_LOG(LOG_DEBUG, "Adapter device found: %s", devname); 429 (void) strlcpy(adptnm, devname, SCSI_MAX_NAME-2); 430 adptnm[SCSI_MAX_NAME-1] = '\0'; 431 sid = lun = -1; 432 433 scsi_find_sid_lun(sa, devname, &sid, &lun); 434 if (ndmp_open_list_find(devname, sid, lun) == NULL && 435 (devid = open(devname, O_RDWR | O_NDELAY)) < 0) { 436 NDMP_LOG(LOG_ERR, "Failed to open device %s: %m.", 437 devname); 438 err = NDMP_NO_DEVICE_ERR; 439 } 440 } else { 441 NDMP_LOG(LOG_ERR, "%s: No such SCSI adapter.", devname); 442 err = NDMP_NO_DEVICE_ERR; 443 } 444 445 if (err != NDMP_NO_ERR) { 446 scsi_open_send_reply(connection, err); 447 return; 448 } 449 450 switch (ndmp_open_list_add(connection, adptnm, sid, lun, devid)) { 451 case 0: 452 /* OK */ 453 break; 454 case EBUSY: 455 err = NDMP_DEVICE_BUSY_ERR; 456 break; 457 case ENOMEM: 458 err = NDMP_NO_MEM_ERR; 459 break; 460 default: 461 err = NDMP_IO_ERR; 462 } 463 if (err != NDMP_NO_ERR) { 464 scsi_open_send_reply(connection, err); 465 return; 466 } 467 468 (void) strlcpy(session->ns_scsi.sd_adapter_name, adptnm, SCSI_MAX_NAME); 469 session->ns_scsi.sd_is_open = 1; 470 session->ns_scsi.sd_devid = devid; 471 if (sid != -1) { 472 session->ns_scsi.sd_sid = sid; 473 session->ns_scsi.sd_lun = lun; 474 session->ns_scsi.sd_valid_target_set = TRUE; 475 } else { 476 session->ns_scsi.sd_sid = session->ns_scsi.sd_lun = -1; 477 session->ns_scsi.sd_valid_target_set = FALSE; 478 } 479 480 scsi_open_send_reply(connection, err); 481 } 482 483 484 /* 485 * common_set_target 486 * 487 * Set the SCSI target (SCSI number, LUN number, controller number) 488 * 489 * Parameters: 490 * connection (input) - connection handle. 491 * device (input) - device name. 492 * controller (input) - controller number. 493 * sid (input) - SCSI target ID. 494 * lun (input) - LUN number. 495 * 496 * Returns: 497 * 0: on success 498 * -1: otherwise 499 */ 500 /*ARGSUSED*/ 501 static void 502 common_set_target(ndmp_connection_t *connection, char *device, 503 ushort_t controller, ushort_t sid, ushort_t lun) 504 { 505 ndmp_scsi_set_target_reply reply; 506 ndmpd_session_t *session = ndmp_get_client_data(connection); 507 int type; 508 509 reply.error = NDMP_NO_ERR; 510 511 if (session->ns_scsi.sd_is_open == -1) { 512 reply.error = NDMP_DEV_NOT_OPEN_ERR; 513 } else if (!scsi_dev_exists(session->ns_scsi.sd_adapter_name, sid, 514 lun)) { 515 NDMP_LOG(LOG_ERR, "No such SCSI device: target %d lun %d.", 516 sid, lun); 517 reply.error = NDMP_NO_DEVICE_ERR; 518 } else { 519 type = scsi_get_devtype(session->ns_scsi.sd_adapter_name, sid, 520 lun); 521 if (type != DTYPE_SEQUENTIAL && type != DTYPE_CHANGER) { 522 NDMP_LOG(LOG_ERR, 523 "Not a tape or robot device: target %d lun %d.", 524 sid, lun); 525 reply.error = NDMP_ILLEGAL_ARGS_ERR; 526 } 527 } 528 529 if (reply.error != NDMP_NO_ERR) { 530 ndmp_send_reply(connection, (void *) &reply, 531 "sending scsi_set_target reply"); 532 return; 533 } 534 535 /* 536 * The open_list must be updated if the SID or LUN are going to be 537 * changed. Close uses the same SID & LUN for removing the entry 538 * from the open_list. 539 */ 540 if (sid != session->ns_scsi.sd_sid || lun != session->ns_scsi.sd_lun) { 541 switch (ndmp_open_list_add(connection, 542 session->ns_scsi.sd_adapter_name, sid, lun, 0)) { 543 case 0: 544 (void) ndmp_open_list_del(session-> 545 ns_scsi.sd_adapter_name, session->ns_scsi.sd_sid, 546 session->ns_scsi.sd_lun); 547 break; 548 case EBUSY: 549 reply.error = NDMP_DEVICE_BUSY_ERR; 550 break; 551 case ENOMEM: 552 reply.error = NDMP_NO_MEM_ERR; 553 break; 554 default: 555 reply.error = NDMP_IO_ERR; 556 } 557 } 558 559 if (reply.error == NDMP_NO_ERR) { 560 NDMP_LOG(LOG_DEBUG, "Updated sid %d lun %d", sid, lun); 561 session->ns_scsi.sd_sid = sid; 562 session->ns_scsi.sd_lun = lun; 563 session->ns_scsi.sd_valid_target_set = TRUE; 564 } 565 566 ndmp_send_reply(connection, (void *) &reply, 567 "sending scsi_set_target reply"); 568 } 569 570 /* 571 * scsi_find_sid_lun 572 * 573 * gets the adapter, and returns the sid and lun number 574 */ 575 void 576 scsi_find_sid_lun(scsi_adapter_t *sa, char *devname, int *sid, int *lun) 577 { 578 scsi_link_t *sl; 579 char *name; 580 581 for (sl = sa->sa_link_head.sl_next; sl && sl != &sa->sa_link_head; 582 sl = sl->sl_next) { 583 name = sasd_slink_name(sl); 584 if (strcmp(devname, name) == 0) { 585 *sid = sl->sl_sid; 586 *lun = sl->sl_lun; 587 return; 588 } 589 } 590 591 *sid = -1; 592 *lun = -1; 593 } 594