1 /*
   2  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
   3  */
   4 
   5 /*
   6  * BSD 3 Clause License
   7  *
   8  * Copyright (c) 2007, The Storage Networking Industry Association.
   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  *      - Redistributions of source code must retain the above copyright
  14  *        notice, this list of conditions and the following disclaimer.
  15  *
  16  *      - Redistributions in binary form must reproduce the above copyright
  17  *        notice, this list of conditions and the following disclaimer in
  18  *        the documentation and/or other materials provided with the
  19  *        distribution.
  20  *
  21  *      - Neither the name of The Storage Networking Industry Association (SNIA)
  22  *        nor the names of its contributors may be used to endorse or promote
  23  *        products derived from this software without specific prior written
  24  *        permission.
  25  *
  26  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  27  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  29  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  30  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  36  * POSSIBILITY OF SUCH DAMAGE.
  37  */
  38 #include <sys/errno.h>
  39 #include <sys/types.h>
  40 #include <stdlib.h>
  41 #include <unistd.h>
  42 #include <ctype.h>
  43 #include <sys/byteorder.h>
  44 #include <sys/scsi/impl/uscsi.h>
  45 #include <sys/scsi/scsi.h>
  46 #include <tlm.h>
  47 #include <pthread.h>
  48 #include "tlm_proto.h"
  49 
  50 /*
  51  * generic routine to read a SCSI page
  52  */
  53 int
  54 read_scsi_page(scsi_link_t *slink, union scsi_cdb *cdb,
  55     int command_size, caddr_t data, int size)
  56 {
  57         struct uscsi_cmd uscsi_cmd;
  58         char *dname;
  59         int dev;
  60 
  61         if (slink == 0 || slink->sl_sa == 0)
  62                 return (EINVAL);
  63 
  64         (void) memset(&uscsi_cmd, 0, sizeof (uscsi_cmd));
  65 
  66         /* Lun is in the 5th bit */
  67         cdb->scc_lun = slink->sl_lun;
  68         uscsi_cmd.uscsi_flags |= USCSI_READ | USCSI_ISOLATE;
  69         uscsi_cmd.uscsi_bufaddr = data;
  70         uscsi_cmd.uscsi_buflen = size;
  71         uscsi_cmd.uscsi_timeout = 1000;
  72         uscsi_cmd.uscsi_cdb = (char *)cdb;
  73 
  74         if (cdb->scc_cmd == SCMD_READ_ELEMENT_STATUS) {
  75                 uscsi_cmd.uscsi_flags |= USCSI_RQENABLE;
  76                 uscsi_cmd.uscsi_rqbuf = data;
  77                 uscsi_cmd.uscsi_rqlen = size;
  78         }
  79         uscsi_cmd.uscsi_cdblen = command_size;
  80 
  81         dname = sasd_slink_name(slink);
  82         dev = open(dname, O_RDWR | O_NDELAY);
  83         if (dev == -1) {
  84                 NDMP_LOG(LOG_DEBUG, "Open failed for %s err=%d",
  85                     dname, errno);
  86                 return (errno);
  87         }
  88         if (tlm_ioctl(dev, USCSICMD, &uscsi_cmd) < 0) {
  89                 NDMP_LOG(LOG_DEBUG, "SCSI cmd %d failed for %s err=%d",
  90                     cdb->scc_cmd, dname, errno);
  91                 (void) close(dev);
  92                 return (errno);
  93         }
  94         (void) close(dev);
  95         return (uscsi_cmd.uscsi_status);
  96 }
  97 
  98 /*
  99  * Read the Inquiry Page.
 100  */
 101 static int
 102 read_inquiry_page(scsi_link_t *slink, struct scsi_inquiry *inq)
 103 {
 104         union scsi_cdb cdb;
 105 
 106         (void) memset(&cdb, 0, sizeof (union scsi_cdb));
 107         cdb.scc_cmd = SCMD_INQUIRY;
 108         cdb.g0_count0 = sizeof (struct scsi_inquiry);
 109 
 110         return (read_scsi_page(slink, &cdb, CDB_GROUP0,
 111             (caddr_t)inq, sizeof (*inq)) ? -1 : 0);
 112 }
 113 
 114 /*
 115  * Read the Product Data Page.
 116  */
 117 static int
 118 read_data_page(scsi_link_t *slink, int pcode, char *snum, int size)
 119 {
 120         char cmd[CDB_GROUP0];
 121 
 122         (void) memset(cmd, 0, sizeof (cmd));
 123 
 124         cmd[0] = SCMD_INQUIRY;
 125         cmd[1] = pcode ? 0x01 : 0x00;
 126         cmd[2] = pcode;
 127         cmd[4] = size;
 128 
 129         /* LINTED improper alignment */
 130         return (read_scsi_page(slink, (union scsi_cdb *)&cmd, CDB_GROUP0,
 131             (caddr_t)snum, size) == -1 ? -1 : 0);
 132 }
 133 
 134 
 135 /*
 136  * Read the Serial Number Page.
 137  */
 138 static int
 139 read_serial_num_page(scsi_link_t *slink, char *snum, int size)
 140 {
 141         scsi_serial_t serial;
 142         int rv;
 143 
 144         (void) memset(&serial, 0, sizeof (scsi_serial_t));
 145         rv = read_data_page(slink, SCSI_SERIAL_PAGE, (caddr_t)&serial,
 146             sizeof (scsi_serial_t));
 147         (void) strlcpy(snum, serial.sr_num, size);
 148 
 149         return (rv == -1 ? -1 : 0);
 150 }
 151 
 152 
 153 /*
 154  * Read the Device Name Page.
 155  */
 156 static int
 157 read_dev_name_page(scsi_link_t *slink, device_ident_header_t *devp, int len)
 158 {
 159         (void) memset(devp, 0, len);
 160 
 161         if (read_data_page(slink, SCSI_DEVICE_IDENT_PAGE, (caddr_t)devp,
 162             len) == -1)
 163                 return (-1);
 164 
 165         if (devp->di_page_code != SCSI_DEVICE_IDENT_PAGE)
 166                 return (-1);
 167 
 168         return (0);
 169 }
 170 
 171 /*
 172  * Formatted print of WWN
 173  */
 174 static void
 175 snprintf_wwn(char *buf, int size, uint8_t *wwn)
 176 {
 177         if (wwn == NULL || buf == NULL)
 178                 return;
 179 
 180         (void) snprintf(buf, size, "0x%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X",
 181             wwn[0], wwn[1], wwn[2], wwn[3], wwn[4], wwn[5], wwn[6], wwn[7]);
 182 }
 183 
 184 
 185 /*
 186  * Extract and print the world wide name (WWN)
 187  */
 188 int
 189 read_device_wwn(scsi_link_t *slink, char *wwnp, int wsize)
 190 {
 191         device_ident_header_t *header;
 192         name_ident_t *ident;
 193         uint16_t page_len = sizeof (device_ident_header_t);
 194         uint16_t act_len;
 195         int accessed;
 196         uint8_t *designator_data;
 197 
 198         (void) memset(wwnp, 0, wsize);
 199 resize:
 200         header = malloc(page_len);
 201         if (header == NULL)
 202                 return (-1);
 203 
 204         if (read_dev_name_page(slink, header, page_len) == -1) {
 205                 free(header);
 206                 return (-1);
 207         }
 208 
 209         act_len = BE_16(header->di_page_length);
 210         if (act_len > page_len) {
 211                 free(header);
 212                 page_len = act_len;
 213                 goto resize;
 214         }
 215 
 216         ident = (name_ident_t *)&header[1];
 217         accessed = sizeof (device_ident_header_t);
 218 
 219         while (accessed < act_len) {
 220 
 221                 accessed += sizeof (name_ident_t);
 222                 accessed += ident->ni_ident_length;
 223                 designator_data = (uint8_t *)&ident[1];
 224                 /*
 225                  * Looking for code set 1 (Binary) ident type NAA 64 bit
 226                  * address that is associated with the node (0).
 227                  */
 228                 if ((ident->ni_code_set == 1) &&
 229                     (ident->ni_ident_type == 3)) {
 230                         snprintf_wwn(wwnp, wsize, designator_data);
 231                         /*
 232                          * If assc is zero (Node) this is the one we want.
 233                          * If we find that we're done.
 234                          */
 235                         if (ident->ni_asso == 0)
 236                                 break;
 237                 }
 238                 /*
 239                  * If we find a EUI-64 we can use that also.
 240                  */
 241                 if ((ident->ni_code_set == 2) &&
 242                     (ident->ni_ident_type == 1) &&
 243                     (ident->ni_asso == 0) &&
 244                     (isprint(wwnp[0] == 0))) { /* Don't overwrite */
 245                         /*
 246                          * This isn't our first choice but we'll print it
 247                          * in case there is nothing else to use.
 248                          */
 249                         (void) snprintf(wwnp, wsize, "%.*s",
 250                             ident->ni_ident_length, designator_data);
 251                 }
 252                 ident =
 253                     (name_ident_t *)&designator_data[ident->ni_ident_length];
 254         }
 255         free(header);
 256         /*
 257          * See if we found something.
 258          * Memset above would leave wwnp not printable.
 259          */
 260         if (isprint(wwnp[0]))
 261                 return (0);
 262         return (-1);
 263 }
 264 
 265 /*
 266  * Add the tape library call back function (used while scanning the bus)
 267  */
 268 static int
 269 add_lib(scsi_link_t *slink, struct scsi_inquiry *sd, void *arg)
 270 {
 271         int l;
 272         int *nlp; /* pointer to library counter */
 273         sasd_drive_t *ssd;
 274 
 275         if (!slink || !sd) {
 276                 NDMP_LOG(LOG_DEBUG, "Invalid argument %x %x %x",
 277                     slink, sd, arg);
 278                 return (-TLM_INVALID);
 279         }
 280 
 281         if (sd->inq_dtype == DTYPE_CHANGER) {
 282                 /* This is a robot, which means this is also a library */
 283                 nlp = (int *)arg;
 284                 (*nlp)++;
 285                 l = tlm_insert_new_library(slink);
 286                 tlm_enable_barcode(l);
 287 
 288                 NDMP_LOG(LOG_DEBUG, "lib %d sid %d lun %d",
 289                     l, slink->sl_sid, slink->sl_lun);
 290 
 291                 if ((ssd = sasd_slink_drive(slink)) != NULL) {
 292                         (void) strlcpy(ssd->sd_vendor, sd->inq_vid,
 293                             sizeof (ssd->sd_vendor));
 294                         (void) strlcpy(ssd->sd_id, sd->inq_pid,
 295                             sizeof (ssd->sd_id));
 296                         (void) strlcpy(ssd->sd_rev, sd->inq_revision,
 297                             sizeof (ssd->sd_rev));
 298                         (void) read_serial_num_page(slink, ssd->sd_serial,
 299                             sizeof (ssd->sd_serial));
 300                         (void) read_device_wwn(slink, ssd->sd_wwn,
 301                             sizeof (ssd->sd_wwn));
 302                 }
 303         }
 304 
 305         return (TLM_NO_ERRORS);
 306 }
 307 
 308 /*
 309  * Create some virutal slots
 310  */
 311 static int
 312 make_virtual_slot(int l, tlm_drive_t *dp)
 313 {
 314         int s;
 315         tlm_slot_t *sp;
 316 
 317         if (l <= 0 || !dp) {
 318                 NDMP_LOG(LOG_DEBUG, "Invalid argument %d, %x", l, dp);
 319                 return (-TLM_INVALID);
 320         }
 321 
 322         if ((s = tlm_insert_new_slot(l)) <= 0)
 323                 return (-TLM_NO_MEMORY);
 324 
 325         if (!(sp = tlm_slot(l, s))) {
 326                 NDMP_LOG(LOG_DEBUG, "Internal error: slot not found %d", s);
 327                 return (-TLM_ERROR_INTERNAL);
 328         }
 329         /*
 330          * For virtual slots element number is 0 and they are always full.
 331          */
 332         sp->ts_element = 0;
 333         sp->ts_status_full = TRUE;
 334         return (TLM_NO_ERRORS);
 335 }
 336 
 337 /*
 338  * Make the tape drive not part of a tape library (stand alone)
 339  */
 340 static int
 341 make_stand_alone_drive(scsi_link_t *slink, int l)
 342 {
 343         int d;
 344         tlm_drive_t *dp;
 345 
 346         if (!slink || l <= 0) {
 347                 NDMP_LOG(LOG_DEBUG, "Invalid argument %x %d", slink, l);
 348                 return (-TLM_INVALID);
 349         }
 350 
 351         d = tlm_insert_new_drive(l);
 352         if (!(dp = tlm_drive(l, d))) {
 353                 NDMP_LOG(LOG_DEBUG, "Internal error: drive not found %d", d);
 354                 return (-TLM_ERROR_INTERNAL);
 355         }
 356 
 357         /* For stand-alone drives, the element number is the drive number. */
 358         dp->td_element = d;
 359         dp->td_slink = slink;
 360         dp->td_scsi_id = slink->sl_sid;
 361         dp->td_lun = slink->sl_lun;
 362         dp->td_exists = TRUE;
 363 
 364         /*
 365          * Note: There is no way to remove library elements.  We cannot clean
 366          * up if make_virtual_slot() fails.
 367          */
 368         (void) make_virtual_slot(l, dp);
 369         return (d);
 370 }
 371 
 372 /*
 373  * Find the LIBRARY structure that has control of this DRIVE.
 374  */
 375 static int
 376 new_drive(scsi_link_t *slink, int *lib)
 377 {
 378         int d;
 379         tlm_drive_t *dp;
 380         tlm_library_t *lp;
 381 
 382         /* Walk through all libraries. */
 383         for (*lib = 1; *lib <= tlm_library_count(); (*lib)++) {
 384                 if (!(lp = tlm_library(*lib)))
 385                         continue;
 386                 /* Walk through drives that are already found. */
 387                 for (d = 1; d <= lp->tl_drive_count; d++) {
 388                         if (!(dp = tlm_drive(*lib, d)))
 389                                 continue;
 390                         if (dp->td_scsi_id == slink->sl_sid &&
 391                             dp->td_lun == slink->sl_lun)
 392                                 return (d);
 393                 }
 394         }
 395 
 396         /* Not part of any library, this is a newly found tape drive. */
 397         return (0);
 398 }
 399 
 400 
 401 /*
 402  * Add the tape library call back function (used while scanning the bus)
 403  */
 404 static int
 405 add_drv(scsi_link_t *slink, struct scsi_inquiry *sd, void *arg)
 406 {
 407         int l, d;
 408         int *vlp; /* pointer to virtual library number */
 409         sasd_drive_t *ssd;
 410         tlm_library_t *library;
 411         tlm_drive_t *drive;
 412 
 413         if (!slink || !sd) {
 414                 NDMP_LOG(LOG_DEBUG, "Invalid argument %x %x %x",
 415                     slink, sd, arg);
 416                 return (-TLM_INVALID);
 417         }
 418 
 419         if (sd->inq_dtype == DTYPE_SEQUENTIAL) {
 420                 vlp = (int *)arg;
 421                 d = new_drive(slink, &l);
 422                 if (d == 0) {
 423                         /* This tape drive was not found inside any robot. */
 424                         if (*vlp == 0) {
 425                                 /*
 426                                  * First, create a virtual library if it's not
 427                                  * done yet.
 428                                  */
 429                                 *vlp = tlm_insert_new_library(slink);
 430                                 if ((library = tlm_library(*vlp)) != NULL)
 431                                         library->tl_capability_robot = FALSE;
 432                         }
 433                         if ((d = make_stand_alone_drive(slink, *vlp)) < 0) {
 434                                 /* sorry, we can not clean up the vlib now * */
 435                                 return (-TLM_INVALID);
 436                         }
 437                         l = *vlp;
 438                         NDMP_LOG(LOG_DEBUG, "vlib(%d, %d) sid %d lun %d",
 439                             l, d, slink->sl_sid, slink->sl_lun);
 440                 } else
 441                         NDMP_LOG(LOG_DEBUG, "(%d, %d) sid %d lun %d",
 442                             l, d, slink->sl_sid, slink->sl_lun);
 443 
 444                 if ((drive = tlm_drive(l, d)) != NULL) {
 445                         drive->td_exists = TRUE;
 446                         drive->td_slink = slink;
 447                 }
 448                 if ((ssd = sasd_slink_drive(slink)) != NULL) {
 449                         (void) strlcpy(ssd->sd_vendor,
 450                             sd->inq_vid, sizeof (ssd->sd_vendor));
 451                         (void) strlcpy(ssd->sd_id, sd->inq_pid,
 452                             sizeof (ssd->sd_id));
 453                         (void) strlcpy(ssd->sd_rev, sd->inq_revision,
 454                             sizeof (ssd->sd_rev));
 455                         (void) read_serial_num_page(slink, ssd->sd_serial,
 456                             sizeof (ssd->sd_serial));
 457                         (void) read_device_wwn(slink, ssd->sd_wwn,
 458                             sizeof (ssd->sd_wwn));
 459                 }
 460         }
 461 
 462         return (TLM_NO_ERRORS);
 463 }
 464 
 465 /*
 466  * Scan the specified bus and call the handler function.
 467  */
 468 static int
 469 scan_bus(scsi_adapter_t *sa, int(*hndlr)(), void *args)
 470 {
 471         int nerr;
 472         scsi_link_t *slink;
 473         struct scsi_inquiry scsi_data;
 474 
 475         nerr = 0;
 476         slink = sa->sa_link_head.sl_next;
 477         for (; slink != &sa->sa_link_head; slink = slink->sl_next) {
 478                 (void) memset(&scsi_data, 0, sizeof (struct scsi_inquiry));
 479                 if (read_inquiry_page(slink, &scsi_data) == -1)
 480                         nerr++;
 481                 else
 482                         if ((*hndlr)(slink, &scsi_data, args) != TLM_NO_ERRORS)
 483                                 nerr++;
 484         }
 485 
 486         return (nerr);
 487 }
 488 
 489 /*
 490  * Marks the library/slots inaccessible if there are not enough drives
 491  * available on the library
 492  */
 493 static void
 494 inaccbl_drv_warn(int start, int max)
 495 {
 496         char *dname;
 497         int l, d;
 498         tlm_library_t *lp;
 499 
 500         for (l = start; l < max; l++) {
 501                 if (!(lp = tlm_library(l)))
 502                         continue;
 503                 if (lp->tl_drive_count <= 0)
 504                         continue;
 505 
 506                 NDMP_LOG(LOG_DEBUG,
 507                     "Warning: The following drives are not accessible:");
 508                 for (d = 1; d <= lp->tl_drive_count; d++)
 509                         if (!(dname = tlm_get_tape_name(l, d))) {
 510                                 NDMP_LOG(LOG_DEBUG,
 511                                     "Error getting drive(%d, %d)", l, d);
 512                         } else
 513                                 NDMP_LOG(LOG_DEBUG, "%s", dname);
 514 
 515                 /*
 516                  * Note: Make the slots inaccessible to prevent running
 517                  * discovery on these libraries.  The better idea is
 518                  * removing these libraries, but we don't have that
 519                  * feature available now.
 520                  */
 521                 lp->tl_slot_count = 0;
 522         }
 523 }
 524 
 525 /*
 526  * Initialize the tape library data structure, asks the libraries what
 527  * equipments they have.
 528  */
 529 int
 530 tlm_init(void)
 531 {
 532         static int nlibs; /* number of found libraries */
 533         int i, nsa;
 534         int l, vlibs, d;
 535         int rv;
 536         scsi_adapter_t *sa;
 537         tlm_library_t *lp;
 538         tlm_drive_t *dp;
 539 
 540         /* Search through all SCSI adapters, look for tape robots. */
 541         nlibs = 0;
 542 
 543         /*
 544          * We probe both changers and tape drives here
 545          * but later on this needs to be removed as the
 546          * probe will happen somewhere else.
 547          */
 548         if (probe_scsi() < 0)
 549                 return (-1);
 550 
 551         nsa = scsi_get_adapter_count();
 552         for (i = 0; i < nsa; i++)
 553                 if ((sa = scsi_get_adapter(i)))
 554                         (void) scan_bus(sa, add_lib, (void *)&nlibs);
 555 
 556         NDMP_LOG(LOG_DEBUG, "nlibs %d", nlibs);
 557 
 558         /* Search through all SCSI adapters, look for tape drives. */
 559         vlibs = 0;
 560         for (i = 0; i < nsa; i++)
 561                 if ((sa = scsi_get_adapter(i)))
 562                         (void) scan_bus(sa, add_drv, (void *)&vlibs);
 563 
 564         NDMP_LOG(LOG_DEBUG, "vlibs %d", vlibs);
 565 
 566         if (nlibs > 0 && vlibs > 0)
 567                 inaccbl_drv_warn(nlibs + 1, vlibs + nlibs + 1);
 568 
 569         for (l = 1; l <= tlm_library_count(); l++) {
 570                 if (!(lp = tlm_library(l))) {
 571                         NDMP_LOG(LOG_DEBUG, "can't find lib %d", l);
 572                         continue;
 573                 }
 574 
 575                 /*
 576                  * Make sure all libraries have tape drives.
 577                  */
 578                 if (lp->tl_drive_count == 0)
 579                         continue;
 580 
 581                 /*
 582                  * Make sure all tape drives exist. A drive that is not
 583                  * linked into the SCSI chain will be seen by the library
 584                  * but we cannot talk to it.
 585                  */
 586                 for (d = 1; d <= lp->tl_drive_count; d++) {
 587                         dp = tlm_drive(l, d);
 588                         if (dp && !dp->td_exists) {
 589                                 NDMP_LOG(LOG_DEBUG, "Ghost drive found %d.%d",
 590                                     l, d);
 591                                 lp->tl_ghost_drives = TRUE;
 592                                 continue;
 593                         }
 594                 }
 595         }
 596 
 597         if (nlibs > 0)
 598                 rv = (vlibs > 0) ? 0 : nlibs;
 599         else
 600                 rv = vlibs;
 601 
 602         return (rv);
 603 }