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