1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. 24 */ 25 26 /* 27 * Copyright 2019 Joyent, Inc. 28 */ 29 30 #include <sys/types.h> 31 #include <sys/scsi/generic/commands.h> 32 #include <sys/scsi/impl/spc3_types.h> 33 34 #include <stddef.h> 35 #include <stdlib.h> 36 #include <string.h> 37 #include <strings.h> 38 #include <alloca.h> 39 #include <stdio.h> 40 #include <unistd.h> 41 #include <dlfcn.h> 42 43 #include <scsi/libscsi.h> 44 #include "libscsi_impl.h" 45 46 int 47 libscsi_assert(const char *expr, const char *file, int line) 48 { 49 char *msg; 50 size_t len; 51 52 len = snprintf(NULL, 0, 53 "ABORT: \"%s\", line %d: assertion failed: %s\n", file, line, expr); 54 55 msg = alloca(len + 1); 56 57 (void) snprintf(msg, len + 1, 58 "ABORT: \"%s\", line %d: assertion failed: %s\n", file, line, expr); 59 60 (void) write(STDERR_FILENO, msg, strlen(msg)); 61 62 abort(); 63 /*NOTREACHED*/ 64 } 65 66 int 67 libscsi_set_errno(libscsi_hdl_t *hp, libscsi_errno_t err) 68 { 69 hp->lsh_errno = err; 70 hp->lsh_errmsg[0] = '\0'; 71 72 return (-1); 73 } 74 75 /* 76 * Internal routine for setting both _ue_errno and _ue_errmsg. We save 77 * and restore the UNIX errno across this routing so the caller can use either 78 * libscsi_set_errno(), libscsi_error(), or libscsi_verror() without this value 79 * changing. 80 */ 81 int 82 libscsi_verror(libscsi_hdl_t *hp, libscsi_errno_t err, const char *fmt, 83 va_list ap) 84 { 85 size_t n; 86 char *errmsg; 87 88 /* 89 * To allow the existing error message to itself be used in an error 90 * message, we put the new error message into a buffer on the stack, 91 * and then copy it into lsh_errmsg. We also need to set the errno, 92 * but because the call to libscsi_set_errno() is destructive to 93 * lsh_errmsg, we do this after we print into our temporary buffer 94 * (in case _libscsi_errmsg is part of the error message) and before we 95 * copy the temporary buffer on to _libscsi_errmsg (to prevent our new 96 * message from being nuked by the call to libscsi_set_errno()). 97 */ 98 errmsg = alloca(sizeof (hp->lsh_errmsg)); 99 (void) vsnprintf(errmsg, sizeof (hp->lsh_errmsg), fmt, ap); 100 (void) libscsi_set_errno(hp, err); 101 102 n = strlen(errmsg); 103 104 if (n != 0 && errmsg[n - 1] == '\n') 105 errmsg[n - 1] = '\0'; 106 107 bcopy(errmsg, hp->lsh_errmsg, n + 1); 108 109 return (-1); 110 } 111 112 /*PRINTFLIKE3*/ 113 int 114 libscsi_error(libscsi_hdl_t *hp, libscsi_errno_t err, const char *fmt, ...) 115 { 116 va_list ap; 117 118 if (fmt == NULL) 119 return (libscsi_set_errno(hp, err)); 120 121 va_start(ap, fmt); 122 err = libscsi_verror(hp, err, fmt, ap); 123 va_end(ap); 124 125 return (err); 126 } 127 128 libscsi_errno_t 129 libscsi_errno(libscsi_hdl_t *hp) 130 { 131 return (hp->lsh_errno); 132 } 133 134 const char * 135 libscsi_errmsg(libscsi_hdl_t *hp) 136 { 137 if (hp->lsh_errmsg[0] == '\0') 138 (void) strlcpy(hp->lsh_errmsg, libscsi_strerror(hp->lsh_errno), 139 sizeof (hp->lsh_errmsg)); 140 141 return (hp->lsh_errmsg); 142 } 143 144 void * 145 libscsi_alloc(libscsi_hdl_t *hp, size_t size) 146 { 147 void *mem; 148 149 if (size == 0) { 150 (void) libscsi_set_errno(hp, ESCSI_ZERO_LENGTH); 151 return (NULL); 152 } 153 154 if ((mem = malloc(size)) == NULL) 155 (void) libscsi_set_errno(hp, ESCSI_NOMEM); 156 157 return (mem); 158 } 159 160 void * 161 libscsi_zalloc(libscsi_hdl_t *hp, size_t size) 162 { 163 void *mem; 164 165 if ((mem = libscsi_alloc(hp, size)) == NULL) 166 return (NULL); 167 168 bzero(mem, size); 169 170 return (mem); 171 } 172 173 char * 174 libscsi_strdup(libscsi_hdl_t *hp, const char *str) 175 { 176 size_t len = strlen(str); 177 char *dup = libscsi_alloc(hp, len + 1); 178 179 if (dup == NULL) 180 return (NULL); 181 182 return (strcpy(dup, str)); 183 } 184 185 /*ARGSUSED*/ 186 void 187 libscsi_free(libscsi_hdl_t *hp, void *ptr) 188 { 189 free(ptr); 190 } 191 192 libscsi_hdl_t * 193 libscsi_init(uint_t version, libscsi_errno_t *errp) 194 { 195 libscsi_hdl_t *hp; 196 197 if ((hp = malloc(sizeof (libscsi_hdl_t))) == NULL) { 198 if (errp != NULL) 199 *errp = ESCSI_NOMEM; 200 return (NULL); 201 } 202 203 bzero(hp, sizeof (libscsi_hdl_t)); 204 hp->lsh_version = version; 205 206 return (hp); 207 } 208 209 void 210 libscsi_fini(libscsi_hdl_t *hp) 211 { 212 libscsi_engine_impl_t *eip, *neip; 213 214 if (hp == NULL) 215 return; 216 217 ASSERT(hp->lsh_targets == 0); 218 219 for (eip = hp->lsh_engines; eip != NULL; eip = neip) { 220 neip = eip->lsei_next; 221 (void) dlclose(eip->lsei_dl_hdl); 222 libscsi_free(hp, eip); 223 } 224 225 free(hp); 226 } 227 228 size_t 229 libscsi_cmd_cdblen(libscsi_hdl_t *hp, uint8_t cmd) 230 { 231 size_t sz; 232 233 switch (CDB_GROUPID(cmd)) { 234 case CDB_GROUPID_0: 235 sz = CDB_GROUP0; 236 break; 237 case CDB_GROUPID_1: 238 sz = CDB_GROUP1; 239 break; 240 case CDB_GROUPID_2: 241 sz = CDB_GROUP2; 242 break; 243 case CDB_GROUPID_3: 244 sz = CDB_GROUP3; 245 break; 246 case CDB_GROUPID_4: 247 sz = CDB_GROUP4; 248 break; 249 case CDB_GROUPID_5: 250 sz = CDB_GROUP5; 251 break; 252 case CDB_GROUPID_6: 253 sz = CDB_GROUP6; 254 break; 255 case CDB_GROUPID_7: 256 sz = CDB_GROUP7; 257 break; 258 default: 259 sz = 0; 260 } 261 262 if (sz == 0) 263 (void) libscsi_error(hp, ESCSI_BADCMD, 264 "unknown or unsupported command %u", cmd); 265 266 return (sz); 267 } 268 269 static char * 270 libscsi_process_inquiry_string(libscsi_hdl_t *hp, const char *raw, size_t len) 271 { 272 char *buf; 273 274 buf = alloca(len + 1); 275 bcopy(raw, buf, len); 276 277 for (; len > 0; len--) { 278 if (buf[len - 1] != ' ') 279 break; 280 } 281 282 buf[len] = '\0'; 283 284 return (libscsi_strdup(hp, buf)); 285 } 286 287 /* 288 * As part of basic initialization, we always retrieve the INQUIRY information 289 * to have the vendor/product/revision information available for all consumers. 290 */ 291 int 292 libscsi_get_inquiry(libscsi_hdl_t *hp, libscsi_target_t *tp) 293 { 294 libscsi_action_t *ap; 295 spc3_inquiry_cdb_t *cp; 296 spc3_inquiry_data_t data; 297 size_t len; 298 299 if ((ap = libscsi_action_alloc(hp, SPC3_CMD_INQUIRY, 300 LIBSCSI_AF_READ | LIBSCSI_AF_SILENT | LIBSCSI_AF_DIAGNOSE, &data, 301 offsetof(spc3_inquiry_data_t, id_vs_36[0]))) == NULL) 302 return (-1); 303 304 cp = (spc3_inquiry_cdb_t *)libscsi_action_get_cdb(ap); 305 306 SCSI_WRITE16(&cp->ic_allocation_length, 307 offsetof(spc3_inquiry_data_t, id_vs_36[0])); 308 309 if (libscsi_exec(ap, tp) != 0 || 310 libscsi_action_get_status(ap) != 0) { 311 libscsi_action_free(ap); 312 return (libscsi_set_errno(hp, ESCSI_INQUIRY_FAILED)); 313 } 314 315 (void) libscsi_action_get_buffer(ap, NULL, NULL, &len); 316 libscsi_action_free(ap); 317 318 if (len < offsetof(spc3_inquiry_data_t, id_vs_36)) 319 return (libscsi_set_errno(hp, ESCSI_INQUIRY_FAILED)); 320 321 if ((tp->lst_vendor = libscsi_process_inquiry_string(hp, 322 data.id_vendor_id, sizeof (data.id_vendor_id))) == NULL || 323 (tp->lst_product = libscsi_process_inquiry_string(hp, 324 data.id_product_id, sizeof (data.id_product_id))) == NULL || 325 (tp->lst_revision = libscsi_process_inquiry_string(hp, 326 data.id_product_revision, 327 sizeof (data.id_product_revision))) == NULL) { 328 return (-1); 329 } 330 331 return (0); 332 } 333 334 const char * 335 libscsi_vendor(libscsi_target_t *tp) 336 { 337 return (tp->lst_vendor); 338 } 339 340 const char * 341 libscsi_product(libscsi_target_t *tp) 342 { 343 return (tp->lst_product); 344 } 345 346 const char * 347 libscsi_revision(libscsi_target_t *tp) 348 { 349 return (tp->lst_revision); 350 }