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 }